{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"colab":{"provenance":[],"gpuType":"T4"},"accelerator":"GPU"},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import warnings\n\n# Masque les avertissements de type UserWarning\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\n\n# ==============================================================================\n# ÉTAPE 1 : Importation des bibliothèques et vérification du GPU \n# Configuration de l'environnement (A placer dans la permière cellule)\n# ==============================================================================\nimport os\n\n# Forcer TensorFlow à utiliser Keras 2 (Legacy) pour éviter les conflits avec Keras\nos.environ[\"TF_USE_LEGACY_KERAS\"] = \"1\"\n\nimport datetime\nimport tensorflow as tf\n\n# Importation stricte des callbacks depuis tf_keras / tensorflow.keras\nfrom tf_keras.callbacks import TensorBoard, EarlyStopping\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow_hub as hub\n\nprint(f\"Version de TensorFlow : {tf.__version__}\")\nprint(f\"Version de TF Hub : {hub.__version__}\")\n\n# Vérification que le GPU est bien activé dans les options du Notebook Kaggle\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    print(f\"GPU activé : {gpus[0].name}\")\nelse:\n    print(\"Attention : Aucun GPU détecté. Activez l'accélérateur GPU dans le panneau 'Notebook options' à droite.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:17.064315Z","iopub.execute_input":"2026-08-10T19:20:17.065060Z","iopub.status.idle":"2026-08-10T19:20:31.826998Z","shell.execute_reply.started":"2026-08-10T19:20:17.065030Z","shell.execute_reply":"2026-08-10T19:20:31.826120Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Learn from Udemy course: [complete-machine-learning-and-data-science-zero-to-mastery](https://github.com/mrdbourke/zero-to-mastery-ml/blob/master/section-4-unstructured-data-projects/end-to-end-dog-vision-video.ipynb)","metadata":{}},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 2 : Chargement des étiquettes (Labels) et gestion des chemins Kaggle\n# ==============================================================================\n# Sur Kaggle, les données d'entrée se trouvent dans '/kaggle/input/competitions/dog-breed-identification'\nDATA_DIR = \"/kaggle/input/competitions/dog-breed-identification\"\nLABELS_PATH = os.path.join(DATA_DIR, \"labels.csv\")\nTRAIN_DIR = os.path.join(DATA_DIR, \"train/\")\n\n# Lecture du fichier CSV dans labels\nlabels_csv = pd.read_csv(LABELS_PATH)\n\n# Liste des chemin d'accès absolus pour chaque image\nfilenames = [os.path.join(TRAIN_DIR, fname + \".jpg\") for fname in labels_csv[\"id\"]]\n\n# Extraction des races uniques et encodage One-Hot (tableaux booléens)\nlabels = labels_csv[\"breed\"].to_numpy()\nunique_breeds = np.unique(labels)\nboolean_labels = [label == unique_breeds for label in labels]\n\nprint(f\"Nombre d'images chargées : {len(filenames)}\")\nprint(f\"Nombre de races unique : {len(unique_breeds)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:31.828399Z","iopub.execute_input":"2026-08-10T19:20:31.829373Z","iopub.status.idle":"2026-08-10T19:20:32.107922Z","shell.execute_reply.started":"2026-08-10T19:20:31.829333Z","shell.execute_reply":"2026-08-10T19:20:32.107023Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 3 : Fonction de pré-traitement des images\n# ==============================================================================\nIMG_SIZE = 224\n\ndef process_image(image_path, img_size=IMG_SIZE):\n    \"\"\"\n    Lit un fichier image, le décode en Tensor RGB, normalise les pixels (0-1)\n    et redimensionne l'image en (224, 224).\n    \"\"\"\n    # Read image file\n    image = tf.io.read_file(image_path)\n    # Decode JPEG to 3 color channels (RGB)\n    image = tf.io.decode_jpeg(image, channels=3)\n    # Convert color channels from 0-255 to 0-1\n    image = tf.image.convert_image_dtype(image, tf.float32)\n    # Resize image to desired shape\n    image = tf.image.resize(image, size=[img_size, img_size])\n    return image\n\ndef get_image_label(image_path, label):\n    \"\"\"\n    Associe une image pré-traitée à son étiquette.\n    \"\"\"\n    image = process_image(image_path)\n    return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:32.109061Z","iopub.execute_input":"2026-08-10T19:20:32.109385Z","iopub.status.idle":"2026-08-10T19:20:32.116693Z","shell.execute_reply.started":"2026-08-10T19:20:32.109351Z","shell.execute_reply":"2026-08-10T19:20:32.115799Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 4 : Création des lots de données (Data Batches)\n# ==============================================================================\nBATCH_SIZE = 32\n\ndef create_data_batches(X, y=None, batch_size=BATCH_SIZE, valid_data=False, test_data=False):\n    \"\"\"\n    Crée des lots de données optimisés pour l'entrainement sur GPU.\n    \"\"\"\n    if test_data:\n        print(\"Création des batches de test...\")\n        data = tf.data.Dataset.from_tensor_slices(tf.constant(X))\n        data_batch = data.map(process_image).batch(batch_size)\n        return data_batch\n\n    elif valid_data:\n        print(\"Création des batches de validation...\")\n        data = tf.data.Dataset.from_tensor_slices((tf.constant(X), tf.constant(y)))\n        data_batch = data.map(get_image_label).batch(batch_size)\n        return data_batch\n\n    else:\n        print(\"Création des batches d'entraînement...\")\n        data = tf.data.Dataset.from_tensor_slices((tf.constant(X), tf.constant(y)))\n        data = data.shuffle(buffer_size=len(X))\n        data = data.map(get_image_label)\n        data_batch = data.batch(batch_size)\n        return data_batch\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:32.118502Z","iopub.execute_input":"2026-08-10T19:20:32.119003Z","iopub.status.idle":"2026-08-10T19:20:32.130978Z","shell.execute_reply.started":"2026-08-10T19:20:32.118933Z","shell.execute_reply":"2026-08-10T19:20:32.130259Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 5 : Séparation des données et génération des batches\n# ==============================================================================\nfrom sklearn.model_selection import train_test_split\n\nNUM_IMAGES = 1000  # Sous-ensemble pour accélérer les tests initialement\n\nX_train, X_val, y_train, y_val = train_test_split(\n    filenames[:NUM_IMAGES],\n    boolean_labels[:NUM_IMAGES],\n    test_size=0.2,\n    random_state=42\n)\n\ntrain_data = create_data_batches(X_train, y_train)\nval_data = create_data_batches(X_val, y_val, valid_data=True)\n\nprint(\"Préparation des données terminée avec succès !\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:32.132132Z","iopub.execute_input":"2026-08-10T19:20:32.132415Z","iopub.status.idle":"2026-08-10T19:20:33.210238Z","shell.execute_reply.started":"2026-08-10T19:20:32.132386Z","shell.execute_reply":"2026-08-10T19:20:33.209313Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 🐶 End-to-end Multil-class Dog Breed Classification\n\nThis notebook builds and end-to-end multi-class image classifier using TensorFlow 2.0 and TensorFlow Hub.\n\n## 1. Problem\n\nIdentifying the breed of a dog given an image of a dog.\n\nWhen I'm sitting at the cafe and I take a photo of a dog, I want to know what breed of dog it is.\n\n## 2. Data\n\nThe data we're using is from Kaggle's dog breed identification competition.\n\n<Link>\n\n## 3. Evaluation\n\nThe evaluation is a file with prediction probabilities for each dog breed of each test image.\n\n<Link>\n\n## 4. Features\n\nSome information about the data:\n* We're dealing with images (unstructured data) so it's probably best we use deep learning/transfer learning.\n* There are 120 breeds of dogs (this means there are 120 different classes).\n* There are around 10,000+ images in the training set (these images have labels).\n* There are around 10,000+ images in the test set (these images have no labels, because we'll want to predict them).","metadata":{"id":"c3TVbaof3NgM"}},{"cell_type":"code","source":"# Unzip the uploaded data into Google Drive\n#!unzip \"/content/drive/MyDrive/Dog Vision/dog-breed-identification.zip\" -d \"/content/drive/MyDrive/Dog Vision\"","metadata":{"id":"tEG9ahp_0Js3","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:33.211143Z","iopub.execute_input":"2026-08-10T19:20:33.212075Z","iopub.status.idle":"2026-08-10T19:20:33.216078Z","shell.execute_reply.started":"2026-08-10T19:20:33.212051Z","shell.execute_reply":"2026-08-10T19:20:33.215185Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Get our workspace ready\n\n* Import TensorFlow 2.x ✅\n* Import TensorFlow Hub ✅\n* Make sure we're using a GPU ✅","metadata":{"id":"NZgyj4uR7a2Z"}},{"cell_type":"code","source":"# # Search: how to import tensorflow in google colab\n# # Import TensorFlow into Colab\n# import tensorflow as tf\n# print(\"TF version:\", tf.__version__)","metadata":{"id":"f99m1FjU3BX1","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:33.217268Z","iopub.execute_input":"2026-08-10T19:20:33.217539Z","iopub.status.idle":"2026-08-10T19:20:33.232143Z","shell.execute_reply.started":"2026-08-10T19:20:33.217515Z","shell.execute_reply":"2026-08-10T19:20:33.231424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Run this cell if TensorFlow 2.x isn't the default in Colab\n# # Import TF 2.x\n# try:\n#     # %tensorflow_version only exists in Colab\n#     %tensorflow_version 2.x\n# except Exception:\n#     pass","metadata":{"id":"FFPy0U1g0xYH","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:33.233211Z","iopub.execute_input":"2026-08-10T19:20:33.234023Z","iopub.status.idle":"2026-08-10T19:20:33.244765Z","shell.execute_reply.started":"2026-08-10T19:20:33.233987Z","shell.execute_reply":"2026-08-10T19:20:33.244008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import tensorflow as tf\n# print(\"TF version:\", tf.__version__)","metadata":{"id":"4n-mmc1H0xqP","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:33.245676Z","iopub.execute_input":"2026-08-10T19:20:33.245982Z","iopub.status.idle":"2026-08-10T19:20:33.254897Z","shell.execute_reply.started":"2026-08-10T19:20:33.245932Z","shell.execute_reply":"2026-08-10T19:20:33.254153Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Import necessary tools\nimport tensorflow as tf\nimport tensorflow_hub as hub\nprint(\"TF version:\", tf.__version__)\nprint(\"TF Hub version:\", hub.__version__)\n\n# Check for GPU availability\nprint(\"GPU\", \"available YESSSS!!!!\" if tf.config.list_physical_devices(\"GPU\") else \"not available :(\")","metadata":{"id":"vdFMV3WL0xvW","outputId":"c7453f57-172c-4c85-b554-fd1d980e56f2","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:33.257398Z","iopub.execute_input":"2026-08-10T19:20:33.257998Z","iopub.status.idle":"2026-08-10T19:20:33.265906Z","shell.execute_reply.started":"2026-08-10T19:20:33.257933Z","shell.execute_reply":"2026-08-10T19:20:33.265344Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Search: what is tensorflow hub","metadata":{"id":"9BjKQ-WV0xx2","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:33.266809Z","iopub.execute_input":"2026-08-10T19:20:33.267155Z","iopub.status.idle":"2026-08-10T19:20:33.275957Z","shell.execute_reply.started":"2026-08-10T19:20:33.267114Z","shell.execute_reply":"2026-08-10T19:20:33.275334Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Getting our data ready (turning into Tensors)\n\nWith all machine learning models, our data has to be in numerical format. So that's what we'll be doing first. Turning our images into Tensors (numerical representations).\n\nLet's start by accessing our data and checking our labels.","metadata":{"id":"FJTSS3SjFjwd"}},{"cell_type":"code","source":"# Checkout the labels of our data\nimport pandas as pd\nlabels_csv = pd.read_csv(\"/kaggle/input/competitions/dog-breed-identification/labels.csv\")\nprint(labels_csv.describe())\nprint(labels_csv.head())","metadata":{"id":"TTaUf3gi0x0e","outputId":"6563567a-44a3-4cb2-b926-be909965be81","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:33.276743Z","iopub.execute_input":"2026-08-10T19:20:33.277236Z","iopub.status.idle":"2026-08-10T19:20:33.321567Z","shell.execute_reply.started":"2026-08-10T19:20:33.277186Z","shell.execute_reply":"2026-08-10T19:20:33.320718Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_csv.head()","metadata":{"id":"gD0KotSE0x6I","outputId":"965c35b0-828e-4ce9-f01c-f4982e2416bc","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:33.322677Z","iopub.execute_input":"2026-08-10T19:20:33.323025Z","iopub.status.idle":"2026-08-10T19:20:33.337712Z","shell.execute_reply.started":"2026-08-10T19:20:33.322990Z","shell.execute_reply":"2026-08-10T19:20:33.337025Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# How many images are there of each breed?\nlabels_csv[\"breed\"].value_counts().plot.bar(figsize=(20, 10))","metadata":{"id":"nA5giHckgKGd","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:33.338615Z","iopub.execute_input":"2026-08-10T19:20:33.338831Z","iopub.status.idle":"2026-08-10T19:20:34.428470Z","shell.execute_reply.started":"2026-08-10T19:20:33.338799Z","shell.execute_reply":"2026-08-10T19:20:34.427664Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_csv[\"breed\"].value_counts().median()","metadata":{"id":"mfUeY4xzIv6F","outputId":"4a13725e-f8dc-4bef-ae2c-3bd16a95acd9","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.429489Z","iopub.execute_input":"2026-08-10T19:20:34.429773Z","iopub.status.idle":"2026-08-10T19:20:34.436128Z","shell.execute_reply.started":"2026-08-10T19:20:34.429741Z","shell.execute_reply":"2026-08-10T19:20:34.435416Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Search: google minimum number of images per class: Google Cloud: Preparing your training data","metadata":{"id":"3xqJAv8EJMu_","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.437182Z","iopub.execute_input":"2026-08-10T19:20:34.437495Z","iopub.status.idle":"2026-08-10T19:20:34.447976Z","shell.execute_reply.started":"2026-08-10T19:20:34.437460Z","shell.execute_reply":"2026-08-10T19:20:34.447269Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's view an image\nfrom IPython.display import Image\nImage(\"/kaggle/input/competitions/dog-breed-identification/train/001513dfcb2ffafc82cccf4d8bbaba97.jpg\")","metadata":{"id":"pR6hlf7BJN6J","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.448988Z","iopub.execute_input":"2026-08-10T19:20:34.449689Z","iopub.status.idle":"2026-08-10T19:20:34.472642Z","shell.execute_reply.started":"2026-08-10T19:20:34.449655Z","shell.execute_reply":"2026-08-10T19:20:34.471837Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Getting images and their labels\n\nLet's get a list of all of our image file pathnames","metadata":{"id":"sqKX4OdkLGna"}},{"cell_type":"code","source":"labels_csv.head()","metadata":{"id":"6tAX9aa-LdDC","outputId":"3c6ef490-1189-43f0-9dbd-be1e31c11d50","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.473591Z","iopub.execute_input":"2026-08-10T19:20:34.473878Z","iopub.status.idle":"2026-08-10T19:20:34.481726Z","shell.execute_reply.started":"2026-08-10T19:20:34.473845Z","shell.execute_reply":"2026-08-10T19:20:34.480884Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create pathnames from image ID's\n# \"fname\" short for file name\nfilenames = [\"/kaggle/input/competitions/dog-breed-identification/train/\" + fname + \".jpg\" for fname in labels_csv[\"id\"]]\n\n# Check the first 10\nfilenames[:10]","metadata":{"id":"Dj6Vd5EuJPQJ","outputId":"38326f20-245a-441d-c208-84aa256a01bb","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.482622Z","iopub.execute_input":"2026-08-10T19:20:34.482865Z","iopub.status.idle":"2026-08-10T19:20:34.494646Z","shell.execute_reply.started":"2026-08-10T19:20:34.482844Z","shell.execute_reply":"2026-08-10T19:20:34.494012Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nos.listdir(\"/kaggle/input/competitions/dog-breed-identification/train/\")","metadata":{"id":"djf6SgizNxxJ","outputId":"2c7f438e-b04c-4199-9d35-51fd891309d3","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.495454Z","iopub.execute_input":"2026-08-10T19:20:34.495823Z","iopub.status.idle":"2026-08-10T19:20:34.724750Z","shell.execute_reply.started":"2026-08-10T19:20:34.495790Z","shell.execute_reply":"2026-08-10T19:20:34.724042Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check whether number of filenames matches number of actual image files\nimport os\nif len(os.listdir(\"/kaggle/input/competitions/dog-breed-identification/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":{"id":"Np15SIMVJPN5","outputId":"fe888cf9-9b02-432e-9b34-6cc005832236","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.726035Z","iopub.execute_input":"2026-08-10T19:20:34.726373Z","iopub.status.idle":"2026-08-10T19:20:34.735363Z","shell.execute_reply.started":"2026-08-10T19:20:34.726340Z","shell.execute_reply":"2026-08-10T19:20:34.734592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# One more check\nImage(filenames[9000])","metadata":{"id":"4nsGch8tJPLK","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.736487Z","iopub.execute_input":"2026-08-10T19:20:34.736769Z","iopub.status.idle":"2026-08-10T19:20:34.756563Z","shell.execute_reply.started":"2026-08-10T19:20:34.736741Z","shell.execute_reply":"2026-08-10T19:20:34.755891Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_csv[\"breed\"][9000]","metadata":{"id":"35wZb_8WJPIJ","outputId":"0116c38c-4dbf-4743-ad2b-80af9440da79","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.757465Z","iopub.execute_input":"2026-08-10T19:20:34.757751Z","iopub.status.idle":"2026-08-10T19:20:34.762231Z","shell.execute_reply.started":"2026-08-10T19:20:34.757729Z","shell.execute_reply":"2026-08-10T19:20:34.761618Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Since we've got our training image filepaths in a list, let's prepare our labels.","metadata":{"id":"Qa7foCNZPaIz"}},{"cell_type":"code","source":"import numpy as np\nlabels = labels_csv[\"breed\"].to_numpy()\n# labels = np.array(labels)  # does same thing as above\nlabels","metadata":{"id":"x51b6D7cJPFp","outputId":"3bbf9607-84fc-467d-87f6-2218ce16522b","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.763212Z","iopub.execute_input":"2026-08-10T19:20:34.763859Z","iopub.status.idle":"2026-08-10T19:20:34.775342Z","shell.execute_reply.started":"2026-08-10T19:20:34.763827Z","shell.execute_reply":"2026-08-10T19:20:34.774729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(labels)","metadata":{"id":"ssLTCxcSJPDB","outputId":"43f21ab2-84c1-4fa2-9cad-0d8f3967b54b","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.776186Z","iopub.execute_input":"2026-08-10T19:20:34.776484Z","iopub.status.idle":"2026-08-10T19:20:34.788617Z","shell.execute_reply.started":"2026-08-10T19:20:34.776434Z","shell.execute_reply":"2026-08-10T19:20:34.787863Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# See if number of labels matches the number of filenames\nif len(labels) == len(filenames):\n    print(\"Number of labels matches number of filenames!\")\nelse:\n    print(\"Number of labels does not match number of filenames, check data directories!\")","metadata":{"id":"lYcCzhjYJPAJ","outputId":"28e18cef-1b00-403d-bab3-11dc44d6604e","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.789721Z","iopub.execute_input":"2026-08-10T19:20:34.790363Z","iopub.status.idle":"2026-08-10T19:20:34.799885Z","shell.execute_reply.started":"2026-08-10T19:20:34.790341Z","shell.execute_reply":"2026-08-10T19:20:34.799265Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Find the unique label values\nunique_breeds = np.unique(labels)\nlen(unique_breeds)","metadata":{"id":"21qx3bVnJO8x","outputId":"f96b5b96-8318-420e-9eec-ce9b14f2fc03","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.800693Z","iopub.execute_input":"2026-08-10T19:20:34.801022Z","iopub.status.idle":"2026-08-10T19:20:34.816668Z","shell.execute_reply.started":"2026-08-10T19:20:34.800979Z","shell.execute_reply":"2026-08-10T19:20:34.816006Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Turn a single label into an array of booleans\nprint(labels[0])\nlabels[0] == unique_breeds","metadata":{"id":"3s538duuJO2p","outputId":"f2dcefc7-cc24-4560-a0e8-cc17dcd21d8f","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.822417Z","iopub.execute_input":"2026-08-10T19:20:34.822718Z","iopub.status.idle":"2026-08-10T19:20:34.829054Z","shell.execute_reply.started":"2026-08-10T19:20:34.822698Z","shell.execute_reply":"2026-08-10T19:20:34.828300Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Turn every label into a boolean array\nboolean_labels = [label == unique_breeds for label in labels]\nboolean_labels[:2]","metadata":{"id":"3itRoiYtJOzR","outputId":"1aa6b0f0-2da4-4b7e-efaa-c1f442377ef9","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.830027Z","iopub.execute_input":"2026-08-10T19:20:34.830315Z","iopub.status.idle":"2026-08-10T19:20:34.874791Z","shell.execute_reply.started":"2026-08-10T19:20:34.830294Z","shell.execute_reply":"2026-08-10T19:20:34.874260Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(boolean_labels)","metadata":{"id":"nLkKnKi2JN2s","outputId":"3def74c4-9112-4a12-b931-6c73ae66c202","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.875597Z","iopub.execute_input":"2026-08-10T19:20:34.875897Z","iopub.status.idle":"2026-08-10T19:20:34.880897Z","shell.execute_reply.started":"2026-08-10T19:20:34.875868Z","shell.execute_reply":"2026-08-10T19:20:34.880117Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Example: Turning boolean array into integers\nprint(labels[0])  # Original label\nprint(np.where(unique_breeds == labels[0]))  # index where label occurs\nprint(boolean_labels[0].argmax())  # index where label occurs in boolean array\nprint(boolean_labels[0].astype(int))  # there will be a 1 where the sample label occurs","metadata":{"id":"VYOFfhkxJNyh","outputId":"aff7e458-e7ea-4c62-8a3d-e31e5632425f","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.881842Z","iopub.execute_input":"2026-08-10T19:20:34.882169Z","iopub.status.idle":"2026-08-10T19:20:34.892648Z","shell.execute_reply.started":"2026-08-10T19:20:34.882142Z","shell.execute_reply":"2026-08-10T19:20:34.892017Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(labels[2])\nprint(boolean_labels[2].astype(int))","metadata":{"id":"czeOXzpKJNth","outputId":"079afe4b-9efb-4a5b-8bb8-4c3b173674a5","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.893565Z","iopub.execute_input":"2026-08-10T19:20:34.893901Z","iopub.status.idle":"2026-08-10T19:20:34.904572Z","shell.execute_reply.started":"2026-08-10T19:20:34.893852Z","shell.execute_reply":"2026-08-10T19:20:34.903990Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Creating our own validation set\n\nSince the dataset from Kaggle doesn't come with a validation set, we're going to create our own.","metadata":{"id":"nRH0He99UsDg"}},{"cell_type":"code","source":"# Setup X & y variable\nX = filenames\ny = boolean_labels","metadata":{"id":"ofgB0U_0wD-g","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.905794Z","iopub.execute_input":"2026-08-10T19:20:34.906477Z","iopub.status.idle":"2026-08-10T19:20:34.915637Z","shell.execute_reply.started":"2026-08-10T19:20:34.906437Z","shell.execute_reply":"2026-08-10T19:20:34.915006Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(filenames)","metadata":{"id":"0g4O7CMBwD7e","outputId":"cedeec0f-1200-4ee0-8519-790b1bb3bd6f","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.916703Z","iopub.execute_input":"2026-08-10T19:20:34.917310Z","iopub.status.idle":"2026-08-10T19:20:34.927642Z","shell.execute_reply.started":"2026-08-10T19:20:34.917286Z","shell.execute_reply":"2026-08-10T19:20:34.927018Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We're going to start off experimenting with ~1000 images and increase as needed","metadata":{"id":"dTeqRUDUBTW0"}},{"cell_type":"code","source":"# Set number of images to use for experimentging\nNUM_IMAGES = 1000  # @param {type: \"slider\", min:1000, max:10000, step:100}","metadata":{"id":"8G8o5We2Bnhd","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.928611Z","iopub.execute_input":"2026-08-10T19:20:34.929006Z","iopub.status.idle":"2026-08-10T19:20:34.937586Z","shell.execute_reply.started":"2026-08-10T19:20:34.928935Z","shell.execute_reply":"2026-08-10T19:20:34.936855Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's split our data into train and validation sets\nfrom sklearn.model_selection import train_test_split\n\n# Split them into training and validation of total size NUM_IMAGES\nX_train, X_val, y_train, y_val = train_test_split(X[:NUM_IMAGES],\n                                                  y[:NUM_IMAGES],\n                                                  test_size=0.2,\n                                                  random_state=42)\n\nlen(X_train), len(y_train), len(X_val), len(y_val)","metadata":{"id":"6OzIwgV2Ck3n","outputId":"ca2e2a56-7281-48f7-ae89-29e06688d59f","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.938565Z","iopub.execute_input":"2026-08-10T19:20:34.938820Z","iopub.status.idle":"2026-08-10T19:20:34.951816Z","shell.execute_reply.started":"2026-08-10T19:20:34.938798Z","shell.execute_reply":"2026-08-10T19:20:34.951021Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's have geez at the training data\nX_train[:5], y_train[:2]","metadata":{"id":"9-mI6jkYDn1I","outputId":"30bd824d-d22f-4d75-e261-82c1bb059234","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.952744Z","iopub.execute_input":"2026-08-10T19:20:34.952975Z","iopub.status.idle":"2026-08-10T19:20:34.965463Z","shell.execute_reply.started":"2026-08-10T19:20:34.952920Z","shell.execute_reply":"2026-08-10T19:20:34.964689Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Preprocessing Images (turning images into Tensors)\n\nTensors are a numerical representation, kind of like a matrix.\n\n1. Take an image filepath as input\n2. Use TensorFlow to read the file and save it to a variable, `image`\n3. Turn our `image` (a jpg) into Tensors\n4. Normalize our image (convert color channel values from 0-255 to 0-1).\n4. Resize the `image` to be a shape of (224, 224)\n5. Return the modified `image`\n\nSearch: tensorflow data loading\n\nBefore we do, let's see what importing an image looks like.","metadata":{"id":"_mSTOaEoENo0"}},{"cell_type":"code","source":"# Convert image to Numpy array\nfrom matplotlib.pyplot import imread\nimage = imread(filenames[42])\nimage.shape","metadata":{"id":"Dspe1t5uD5KN","outputId":"79194698-643b-4196-8285-e59befe35d24","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.966427Z","iopub.execute_input":"2026-08-10T19:20:34.966793Z","iopub.status.idle":"2026-08-10T19:20:34.985552Z","shell.execute_reply.started":"2026-08-10T19:20:34.966760Z","shell.execute_reply":"2026-08-10T19:20:34.985011Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Search* (CF): what is a color channel\n\nimage.max(), image.min()","metadata":{"id":"WgBh5SIqGY9U","outputId":"d600d98d-9b3e-4298-db3d-1377d9ac68c4","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.986443Z","iopub.execute_input":"2026-08-10T19:20:34.986716Z","iopub.status.idle":"2026-08-10T19:20:34.992463Z","shell.execute_reply.started":"2026-08-10T19:20:34.986687Z","shell.execute_reply":"2026-08-10T19:20:34.991678Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image[:2]","metadata":{"id":"7IBujfKwHL8X","outputId":"c5054e25-38cc-41ae-9464-2e5bbecb4c75","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:34.993487Z","iopub.execute_input":"2026-08-10T19:20:34.993724Z","iopub.status.idle":"2026-08-10T19:20:35.002668Z","shell.execute_reply.started":"2026-08-10T19:20:34.993691Z","shell.execute_reply":"2026-08-10T19:20:35.002013Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Turn image into a tensor\ntf.constant(image)[:2]","metadata":{"id":"m7d46fVxGubl","outputId":"52fece85-8efa-42b1-e5d3-e39657d113f7","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.003451Z","iopub.execute_input":"2026-08-10T19:20:35.003713Z","iopub.status.idle":"2026-08-10T19:20:35.031614Z","shell.execute_reply.started":"2026-08-10T19:20:35.003691Z","shell.execute_reply":"2026-08-10T19:20:35.030964Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now we've seen what an image looks like as a Tensor, let's make a function to preprocess them.\n\n1. Take an image filepath as input\n2. Use TensorFlow to read the file and save it to a variable, `image`\n3. Turn our `image` (a jpg) into Tensors\n4. Resize the `image` to be a shape of (224, 224)\n5. Return the modified `image`\n","metadata":{"id":"m3tYhMt9HntA"}},{"cell_type":"code","source":"# Define image size\nIMG_SIZE = 224\n\n# Create a function for preprocessing images\ndef process_image(image_path, img_size=IMG_SIZE):\n    \"\"\"\n    Take an image file path and turns the image into a Tensor.\n    \"\"\"\n    # Read in an image file\n    image = tf.io.read_file(image_path)\n    # Turn the jpeg image into numerical Tensor with 3 colour channels (Red, Green, Blue)\n    image = tf.image.decode_jpeg(image, channels=3)\n    # Convert the colour channel values from 0-225 to 0-1 values\n    image = tf.image.convert_image_dtype(image, tf.float32)\n    # Resize the image to our desired value (224, 224)\n    image = tf.image.resize(image, size=[IMG_SIZE, IMG_SIZE])\n\n    return image","metadata":{"id":"pWusvYasG3e9","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.032425Z","iopub.execute_input":"2026-08-10T19:20:35.032687Z","iopub.status.idle":"2026-08-10T19:20:35.037411Z","shell.execute_reply.started":"2026-08-10T19:20:35.032665Z","shell.execute_reply":"2026-08-10T19:20:35.036657Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"CF: yann lecun batch size ; jeremy howard batch size\n\n## Turning our data into batches\n\nWhy turn our data into batches?\n\nLet's say you're trying to process 10,000+ images in one go... they all might not fit into memory.\n\nSo that's why we do about 32 (this is the batch size) images at a time (you can manually adjust the batch size if need be).\n\nIn order to use TensorFlow effectively, we need our data in the form of Tensor tuples which look like this: `(image, label)`.","metadata":{"id":"tIXU111PjRVj"}},{"cell_type":"code","source":"# Create a simple function to return a tuple (image, lable)\ndef get_image_label(image_path, label):\n    \"\"\"\n    Take an image file path name and the associated label,\n    processes the image and returns a tuple of (image, lable).\n    \"\"\"\n    image =process_image(image_path)\n    return image, label","metadata":{"id":"7nR_kG6_MgAg","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.038374Z","iopub.execute_input":"2026-08-10T19:20:35.039117Z","iopub.status.idle":"2026-08-10T19:20:35.047821Z","shell.execute_reply.started":"2026-08-10T19:20:35.039094Z","shell.execute_reply":"2026-08-10T19:20:35.047112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Demo of the above\n(process_image(X[42]), tf.constant(y[42]))","metadata":{"id":"BdDfu1ZLmaVf","outputId":"d6750f9b-f58f-4586-819d-93f676843a54","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.048821Z","iopub.execute_input":"2026-08-10T19:20:35.049199Z","iopub.status.idle":"2026-08-10T19:20:35.084129Z","shell.execute_reply.started":"2026-08-10T19:20:35.049165Z","shell.execute_reply":"2026-08-10T19:20:35.083393Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now we've got a way to turn our data into tuples of Tensors in the form: `(image, label)`, let's make a function to turn all of our data (`x` & `y`) into batches!","metadata":{"id":"Ku1oufGApnFB"}},{"cell_type":"code","source":"# Define the batch size, 32 is a good start\nBATCH_SIZE = 32\n\n# Create a function to turn data into batches\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 it 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 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)\n        return data_batch\n\n    # If the data is a valid dataset, we don't need to shuffle it\n    elif valid_data:\n        print(\"Creating validation data batches...\")\n        data = tf.data.Dataset.from_tensor_slices((tf.constant(X),  # filepaths\n                                                   tf.constant(y)))  # labels\n\n        data_batch = data.map(get_image_label).batch(BATCH_SIZE)\n        return data_batch\n\n    else:\n        print(\"Creating training data batches...\")\n        # Turn filepaths and labels into Tensors\n        data = tf.data.Dataset.from_tensor_slices((tf.constant(X),\n                                                   tf.constant(y)))\n        # Shuffling pathnames and labels before mapping image processor function is faster than shuffling images\n        data = data.shuffle(buffer_size=len(X))\n\n        # Create (image, label) tuples (this also turns the image path into a preprocessed image)\n        data = data.map(get_image_label)\n\n        # Turn the training data into batches\n        data_batch = data.batch(BATCH_SIZE)\n\n    return data_batch","metadata":{"id":"siHfG2Cxnai1","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.084933Z","iopub.execute_input":"2026-08-10T19:20:35.085263Z","iopub.status.idle":"2026-08-10T19:20:35.092074Z","shell.execute_reply.started":"2026-08-10T19:20:35.085220Z","shell.execute_reply":"2026-08-10T19:20:35.091441Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Creating training and validation data batches\ntrain_data = create_data_batches(X_train, y_train)\nval_data = create_data_batches(X_val, y_val, valid_data=True)","metadata":{"id":"BjlbX1b8v9pR","outputId":"baa3041b-2550-4b3c-a6ec-2c8b5779daf1","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.093048Z","iopub.execute_input":"2026-08-10T19:20:35.093365Z","iopub.status.idle":"2026-08-10T19:20:35.186700Z","shell.execute_reply.started":"2026-08-10T19:20:35.093344Z","shell.execute_reply":"2026-08-10T19:20:35.186050Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check out the different attributes of our data batches\ntrain_data.element_spec, val_data.element_spec","metadata":{"id":"GoEzX6KIwYwg","outputId":"93b3cd6e-d11e-4f50-acd9-ffd687098d31","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.187648Z","iopub.execute_input":"2026-08-10T19:20:35.187917Z","iopub.status.idle":"2026-08-10T19:20:35.193207Z","shell.execute_reply.started":"2026-08-10T19:20:35.187894Z","shell.execute_reply":"2026-08-10T19:20:35.192421Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Visualizing Data Batches\n\nOur data is now in batches, however, these can be a little hard to understand/comprehend, let's visualize them!","metadata":{"id":"ararw6tGxDAu"}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Create a function for viewing images in a data batch\ndef show_25_images(images, labels):\n    \"\"\"\n    Displays a plot of 25 images and their labels from a data batch.\n    \"\"\"\n    # Setup the figure\n    plt.figure(figsize=(10, 10))\n    # Loop through 25 (for displaying 25 images)\n    for i in range(25):\n        # Create subplots (5 rows, 5 columns)\n        ax = plt.subplot(5, 5, i+1)\n        # Display an image\n        plt.imshow(images[i])\n        # Add the image label as the title\n        plt.title(unique_breeds[labels[i].argmax()])\n        # Turn the grid lines off\n        plt.axis(\"off\")","metadata":{"id":"AAfIkfQdwpXu","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.194173Z","iopub.execute_input":"2026-08-10T19:20:35.194911Z","iopub.status.idle":"2026-08-10T19:20:35.208378Z","shell.execute_reply.started":"2026-08-10T19:20:35.194879Z","shell.execute_reply":"2026-08-10T19:20:35.207512Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_data","metadata":{"id":"FW4Tu0z8zxF_","outputId":"7a1c4f7d-4d68-4d2d-ac51-63092b036a12","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.209313Z","iopub.execute_input":"2026-08-10T19:20:35.209590Z","iopub.status.idle":"2026-08-10T19:20:35.221459Z","shell.execute_reply.started":"2026-08-10T19:20:35.209568Z","shell.execute_reply":"2026-08-10T19:20:35.220838Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Now let's visualize the data in a training batch\ntrain_images, train_labels = next(train_data.as_numpy_iterator())\nshow_25_images(train_images, train_labels)","metadata":{"id":"wQxJ_5II0i4F","outputId":"94709492-575c-446e-c123-e9d3cbc6ab34","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:35.222381Z","iopub.execute_input":"2026-08-10T19:20:35.222665Z","iopub.status.idle":"2026-08-10T19:20:36.569536Z","shell.execute_reply.started":"2026-08-10T19:20:35.222633Z","shell.execute_reply":"2026-08-10T19:20:36.568541Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(train_images), len(train_labels)","metadata":{"id":"_A3pHfYb0bGd","outputId":"093d27f2-4846-465f-81b3-e2d560e2fe78","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:36.570833Z","iopub.execute_input":"2026-08-10T19:20:36.571150Z","iopub.status.idle":"2026-08-10T19:20:36.577456Z","shell.execute_reply.started":"2026-08-10T19:20:36.571117Z","shell.execute_reply":"2026-08-10T19:20:36.576641Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"unique_breeds[y[0].argmax()]","metadata":{"id":"t3t99lu8zHOw","outputId":"2aaa7ad6-f0e3-486d-f96e-1e4d8aef21c2","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:36.579157Z","iopub.execute_input":"2026-08-10T19:20:36.579426Z","iopub.status.idle":"2026-08-10T19:20:36.589499Z","shell.execute_reply.started":"2026-08-10T19:20:36.579405Z","shell.execute_reply":"2026-08-10T19:20:36.588740Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Now let's visualize our validation set\nval_images, val_labels = next(val_data.as_numpy_iterator())\nshow_25_images(val_images, val_labels)","metadata":{"id":"eRoz6-Z8zQPA","outputId":"2356a02e-5b6d-44aa-d5f4-8a1294946d7c","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:36.590549Z","iopub.execute_input":"2026-08-10T19:20:36.590824Z","iopub.status.idle":"2026-08-10T19:20:38.136128Z","shell.execute_reply.started":"2026-08-10T19:20:36.590794Z","shell.execute_reply":"2026-08-10T19:20:38.135036Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Building a model\n\nBefore we build a model, there are a few things we need to define:\n* The input shape (our images shape, in the form of Tensors) to our model.\n* The output shape (image labels, in the form of Tensors) of our model.\n* The URL of the model we want to use from TensorFlow Hub - <https://www.kaggle.com/models/google/mobilenet-v2/tensorFlow2/130-224-classification>.","metadata":{"id":"NuV6R0LA2VHY"}},{"cell_type":"code","source":"IMG_SIZE","metadata":{"id":"Z6oNLrp33qlw","outputId":"05815f0f-8c8b-4f8e-9c27-3b65fa002e1d","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:38.137323Z","iopub.execute_input":"2026-08-10T19:20:38.137674Z","iopub.status.idle":"2026-08-10T19:20:38.143168Z","shell.execute_reply.started":"2026-08-10T19:20:38.137650Z","shell.execute_reply":"2026-08-10T19:20:38.142191Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Setup input shape to the model\nINPUT_SHAPE = [None, IMG_SIZE, IMG_SIZE, 3]  # Batch, height, width, colour channels\n\n# Setup output shape of our model\nOUTPUT_SHAPE = len(unique_breeds)\n\n# Setup model URL from TensorFlow Hub\nMODEL_URL = \"https://www.kaggle.com/models/google/mobilenet-v2/TensorFlow2/130-224-classification/2\"","metadata":{"id":"KqHElmlr1scq","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:38.144188Z","iopub.execute_input":"2026-08-10T19:20:38.144536Z","iopub.status.idle":"2026-08-10T19:20:38.154130Z","shell.execute_reply.started":"2026-08-10T19:20:38.144513Z","shell.execute_reply":"2026-08-10T19:20:38.153502Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(unique_breeds)","metadata":{"id":"lRzRMt2B4BU0","outputId":"b33a491c-0f76-4cb7-d9e5-de3f4cdf5d72","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:38.155101Z","iopub.execute_input":"2026-08-10T19:20:38.155879Z","iopub.status.idle":"2026-08-10T19:20:38.167393Z","shell.execute_reply.started":"2026-08-10T19:20:38.155845Z","shell.execute_reply":"2026-08-10T19:20:38.166785Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"CF :\n* pytorch hub\n* model zoo\n* papers with code\n* scikitlearn ml map: choosing the right estimator","metadata":{"id":"GAHunpeW50ps"}},{"cell_type":"markdown","source":"CF:\n* tensorflow keras\n* Keras Sequential model\n* Keras fonctional API","metadata":{"id":"qimmlFFhCneS"}},{"cell_type":"markdown","source":"Now we've got our inputs, outputs and model ready to go.\nLet's put them together into a Keras deep learinig model!\n\nKnowing this, let's create a function which:\n* Takes the input shape, output shape and the model we've chosen as parameters.\n* Defines the layers in a Keras model in sequential fashion (do this first, then this, then that).\n* Compiles the model (says it should be evaluated and improved).\n* Builds the model (tells the model the input shape it'll be getting).\n* Returns the model.\n\nAll of these steps can be found here: <https://www.tensorflow.org/guide/keras>","metadata":{"id":"4DR3JXwfDPKo"}},{"cell_type":"code","source":"#import tensorflow_hub as hub\n#import tensorflow as tf\nimport tf_keras  # Use tf_keras instead of tf.keras\n\n# Create a function which builds a Keras model\ndef create_model(input_shape=INPUT_SHAPE, output_shape=OUTPUT_SHAPE, model_url=MODEL_URL):\n    print(\"Building model with:\", MODEL_URL)\n\n    # Setup the model layers\n    model = tf_keras.Sequential([\n        hub.KerasLayer(MODEL_URL),  # Layer 1 (input layer)\n        tf_keras.layers.Dense(units=OUTPUT_SHAPE,\n                                  activation=\"softmax\")  # Layer 2 (output layer)\n    ])\n\n    # Compile the model\n    model.compile(\n        loss=tf.keras.losses.CategoricalCrossentropy(),\n        optimizer=tf_keras.optimizers.Adam(),\n        metrics=[\"accuracy\"]\n    )\n\n    # Build the model\n    model.build(INPUT_SHAPE)\n\n    return model","metadata":{"id":"DwQhKuuuKg8P","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:38.168294Z","iopub.execute_input":"2026-08-10T19:20:38.168572Z","iopub.status.idle":"2026-08-10T19:20:38.179118Z","shell.execute_reply.started":"2026-08-10T19:20:38.168541Z","shell.execute_reply":"2026-08-10T19:20:38.178494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = create_model()\nmodel.summary()","metadata":{"id":"Ff5yNrAWJW9h","outputId":"8a53c96d-4604-4711-c45f-aa40aa418044","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:38.180047Z","iopub.execute_input":"2026-08-10T19:20:38.180346Z","iopub.status.idle":"2026-08-10T19:20:43.417988Z","shell.execute_reply.started":"2026-08-10T19:20:38.180324Z","shell.execute_reply":"2026-08-10T19:20:43.417232Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"CF:\n* mobile net v2 architecture\n* What is a convolutional neural network","metadata":{"id":"rWLheQUZP94j"}},{"cell_type":"code","source":"outputs = np.ones(shape=(1, 1, 1280))\noutputs","metadata":{"id":"JGsT0EWJJczB","outputId":"9ef6781e-fdca-4f5a-b50f-f0ba44d6941f","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:43.419003Z","iopub.execute_input":"2026-08-10T19:20:43.420253Z","iopub.status.idle":"2026-08-10T19:20:43.425457Z","shell.execute_reply.started":"2026-08-10T19:20:43.420228Z","shell.execute_reply":"2026-08-10T19:20:43.424732Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"CF:\n* what is softmax: \"[...]but after applying softmax, each component will be in the interval (0, 1), and the compoents will add up to 1, [...]\"\n* What loss function should I use\n* ImageNet\n\n\nDefine a model how it going to learn:\n* `loss`: The loss is a measure of how well the model is guessing. So when it's trying to make a prediction whilst it's learning, the higher the loss, the worse the prediction is. E.g., At the International Hill Descending Championships, the higher you are, the worse off you're doing.\n\n* `Adam optimizer`: is like your friend Adam tells you how to walk down the hill. It's a function that helps your model improve its guesses.\n\n* `metrics`: is like a juge evaluation how well you're doing. They know where you need to end up. So they compare how you're doing, to where you're supposed to be. And their comparison is how you get scored. AKA, your accuracy at getting down the hill. It's a way of evaluating thoses guesses after it's learned.","metadata":{"id":"x4Ufqae6R64c"}},{"cell_type":"markdown","source":"## Creating callbacks\n\nCallbacks are helper functions a model can use during training to do such things as save its progress, check its progress or stop training early if a model stops improving.\n\nWe'll create two callbacks, one for TensorBoard which helps track our models progress and another for early stopping which prevents our model from training for too long.\n\nCF: tensorflow keras model callbacks\n\n### TensorBoard Callback\n\nTo setup a TensorBoard callback, we need to do 3 things:\n1. Load the TensorBoard notebook extension ✅\n2. Create a TensorBoard callback which is able to save logs to a directory and pass it to our model's `fit()` function. ✅\n3. Visualize our models training logs with the `%tensorboard` magic function (we'll do this after model training).\n","metadata":{"id":"mIANOvPRbHPe"}},{"cell_type":"markdown","source":"## Créer le dossier `logs` dans l'espace de travai Kaggle","metadata":{}},{"cell_type":"code","source":"import os\n\n# 1. Définition du chemin du dossier de logs dans l'espace de travail Kaggle\nLOG_DIR = \"/kaggle/working/logs/\"\n\n# 2. Création automatique du dossier s'il n'existe pas encore\nos.makedirs(LOG_DIR, exist_ok=True)\n\n# 3. Message de confirmation\nprint(f\"Le dossier de logs est prêt à l'emplacement : {LOG_DIR}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:43.426615Z","iopub.execute_input":"2026-08-10T19:20:43.426928Z","iopub.status.idle":"2026-08-10T19:20:43.436439Z","shell.execute_reply.started":"2026-08-10T19:20:43.426894Z","shell.execute_reply":"2026-08-10T19:20:43.435792Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 1 : Chargement de l'extension TensorBoard\n# ==============================================================================\n\n# Load TensorBoard notebook extension\n%load_ext tensorboard","metadata":{"id":"DUB7nxDSRqfc","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:43.437347Z","iopub.execute_input":"2026-08-10T19:20:43.437666Z","iopub.status.idle":"2026-08-10T19:20:43.463558Z","shell.execute_reply.started":"2026-08-10T19:20:43.437633Z","shell.execute_reply":"2026-08-10T19:20:43.463006Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 2 : Lancement de l'interface TensorBoard\n# ==============================================================================\n# Cette commande va lire les fichiers enregistrés dans le dossier '/kaggle/working/logs/'\n%tensorboard --logdir /kaggle/working/logs","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:43.464444Z","iopub.execute_input":"2026-08-10T19:20:43.464722Z","iopub.status.idle":"2026-08-10T19:20:50.006336Z","shell.execute_reply.started":"2026-08-10T19:20:43.464699Z","shell.execute_reply":"2026-08-10T19:20:50.005682Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import datetime\n\n# # Creating a function to build a TensorBoard callback\n# def create_tensorboard_callback():\n#     # Create a log directory for storing TensorBoard logs\n#     logdir = os.path.join(\"/kaggle/working/logs\",\n#                           # Make it so the logs get tracked whenever we run an experiment\n#                           datetime.datetime.now().strftime(\"%Y%m%d-%H%M%S\"))\n#     return tf.keras.callbacks.TensorBoard(logdir)","metadata":{"id":"jviWbuHlhUNG","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.007192Z","iopub.execute_input":"2026-08-10T19:20:50.007508Z","iopub.status.idle":"2026-08-10T19:20:50.011636Z","shell.execute_reply.started":"2026-08-10T19:20:50.007473Z","shell.execute_reply":"2026-08-10T19:20:50.010861Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import os\n# import datetime\n# import tensorflow as tf\n\n# ==============================================================================\n# ÉTAPE 1 : Fonction de création du Callbac TensorBoard\n# ==============================================================================\ndef create_tensorboard_callback():\n    \"\"\"\n    Crée un callback TensorBoard compatible avec Keras et l'environnement Kaggle.\n    Les journaux sont sauvegardés dans '/kaggle/working/logs/' avec un sous-dossier horodaté.\n    \"\"\"\n    # Dossier de destination sur Kaggle\n    log_dir = os.path.join(\n        \"/kaggle/working/logs\",\n         datetime.datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n    )\n\n    print(f\"Journaux TensorBoard enregistrés dans : {log_dir}\")\n\n    # Création du callback avec le paramètre log_dir\n    return TensorBoard(log_dir=log_dir)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.012684Z","iopub.execute_input":"2026-08-10T19:20:50.012870Z","iopub.status.idle":"2026-08-10T19:20:50.030533Z","shell.execute_reply.started":"2026-08-10T19:20:50.012852Z","shell.execute_reply":"2026-08-10T19:20:50.029698Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Early Stopping Callback\n\nEarly stopping helps stop our model from overfitting by stopping training if a certain evaluation metrics stops improving.\n\n<https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/EarlyStopping>","metadata":{"id":"ELiawrxmjXvH"}},{"cell_type":"code","source":"# import tensorflow as tf\n\n# # Create early stopping callback\n# early_stopping = tf.keras.callbacks.EarlyStopping(monitor=\"val_accuracy\",\n#                                                   patience=3)","metadata":{"id":"dwhcUw4Vi7BS","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.031598Z","iopub.execute_input":"2026-08-10T19:20:50.031898Z","iopub.status.idle":"2026-08-10T19:20:50.041798Z","shell.execute_reply.started":"2026-08-10T19:20:50.031876Z","shell.execute_reply":"2026-08-10T19:20:50.041224Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Création du callback EarlyStopping\nearly_stopping = EarlyStopping(\n    monitor=\"val_accuracy\",\n    patience=3,\n    restore_best_weights=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.042858Z","iopub.execute_input":"2026-08-10T19:20:50.043134Z","iopub.status.idle":"2026-08-10T19:20:50.053215Z","shell.execute_reply.started":"2026-08-10T19:20:50.043112Z","shell.execute_reply":"2026-08-10T19:20:50.052578Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training a model (on subset of data)\n\nOur first model is only going to train on 1000 images, to make sure everything is working.","metadata":{"id":"nmkD-nXnlkjy"}},{"cell_type":"code","source":"# NUM_EPOCHS = 100 #@param {type:\"slider\", min:10, max:100, step:10}","metadata":{"id":"mAHtpLBMl1V8","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.054138Z","iopub.execute_input":"2026-08-10T19:20:50.054403Z","iopub.status.idle":"2026-08-10T19:20:50.064159Z","shell.execute_reply.started":"2026-08-10T19:20:50.054372Z","shell.execute_reply":"2026-08-10T19:20:50.063544Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# CONFIGURATION DES HYPERPARAMÈTRE (Adapté pour Kaggle Notebook)\n# ==============================================================================\n\n# 1. Mode débogage (True = test rapide, False = entraînement complet)\nDEBUG_MODE = True\n\n# 2. Nombre d'époques (passages complets sur les données)\nif DEBUG_MODE:\n    NUM_EPOCHS = 3  # Nombre réduit pour tester rapidement le code\n    print(f\"Mode DEBUG activé : Entraînement sur {NUM_EPOCHS} époques.\")\nelse:\n    NUM_EPOCHS = 100  # Entrainement complet une fois le code validé\nprint(f\"Mode PRODUCTION activé : Entraînement sur {NUM_EPOCHS} époques.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.065002Z","iopub.execute_input":"2026-08-10T19:20:50.065337Z","iopub.status.idle":"2026-08-10T19:20:50.075780Z","shell.execute_reply.started":"2026-08-10T19:20:50.065292Z","shell.execute_reply":"2026-08-10T19:20:50.074978Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check to make sure we're still running on a GPU\nprint(\"GPU\", \"available (YES!!!!!!)\" if tf.config.list_physical_devices(\"GPU\") else \"not available :(\")","metadata":{"id":"7D1b92C9k6Id","outputId":"7333717a-3649-46c2-b007-56cf36d57c76","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.076765Z","iopub.execute_input":"2026-08-10T19:20:50.077076Z","iopub.status.idle":"2026-08-10T19:20:50.090166Z","shell.execute_reply.started":"2026-08-10T19:20:50.077055Z","shell.execute_reply":"2026-08-10T19:20:50.089477Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Let's create a function which trains a model.\n* Create a model using `create_model()`\n* Setup a TensorBoard callback using `create_tensorboard_callback()`\n* Call the `fit()` function on our model passing it the training data, validation data, number of epochs to train for (`NUM_EPOCHS`) and the callbacks we'd like to use\n* Return the model","metadata":{"id":"X1IcyUzfnIDR"}},{"cell_type":"code","source":"# # Build a function to train and return a trained model\n# def train_model():\n#     \"\"\"\n#     Trains a given model and returns the trained version.\n#     \"\"\"\n#     # Create a model\n#     model = create_model()\n\n#     # Create new TensorBoard session everytime we train a model\n#     tensorboard = create_tensorboard_callback()\n\n#     # Fit the model to the data passing it the callbacks we created\n#     model.fit(x=train_data,\n#               epochs=NUM_EPOCHS,\n#               validation_data=val_data,\n#               validation_freq=1,\n#               callbacks=[tensorboard, early_stopping])\n#     # Return the fitted model\n#     return model","metadata":{"id":"Wjo9hF24m3eE","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.091224Z","iopub.execute_input":"2026-08-10T19:20:50.091511Z","iopub.status.idle":"2026-08-10T19:20:50.101918Z","shell.execute_reply.started":"2026-08-10T19:20:50.091480Z","shell.execute_reply":"2026-08-10T19:20:50.101274Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 2 : Fonction d'entraînement du modèle\n# ==============================================================================\ndef train_model():\n    \"\"\"\n    Instancie, entraîne et retourne un modèle Keras avec callbacks.\n    \"\"\"\n    # 1. Création de l'architecture du modèle\n    model = create_model()\n\n    # 2. Création des callbacks pour cette session d'entraînement\n    tensorboard = create_tensorboard_callback()\n\n    # 3. Entraînement du modèle sur les données\n    model.fit(\n        x=train_data,\n        epochs=NUM_EPOCHS,\n        validation_data=val_data,\n        validation_freq=1,\n        callbacks=[tensorboard, early_stopping]\n    )\n\n    # 4. Retour du modèle entraîné\n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.103153Z","iopub.execute_input":"2026-08-10T19:20:50.103704Z","iopub.status.idle":"2026-08-10T19:20:50.113645Z","shell.execute_reply.started":"2026-08-10T19:20:50.103671Z","shell.execute_reply":"2026-08-10T19:20:50.112984Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 3 : Lancement de l'entraînement\n# ==============================================================================\n# Fit the model to the data\nmodel = train_model()","metadata":{"id":"ezJiQpc8-hFQ","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:20:50.114769Z","iopub.execute_input":"2026-08-10T19:20:50.115125Z","iopub.status.idle":"2026-08-10T19:21:05.597465Z","shell.execute_reply.started":"2026-08-10T19:20:50.115102Z","shell.execute_reply":"2026-08-10T19:21:05.596717Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Question:** It looks like our model is overfitting because it's performing far better on the training dataset than the validation dataset, what are some ways to prevent model overfitting in deep learning neural networks?\n\n**Note:** Overfitting to begin with is a good thing! It means our model is learning!!!","metadata":{"id":"IE4TT5QT_sWW"}},{"cell_type":"markdown","source":"## Visualiser vos métriques d’entraînement sans TensorBoard : une méthode Python simple et fiable\n\n« Puisque TensorBoard pose problème (ce qui arrive régulièrement dans l'environnement virtuel de Kaggle en raison de l'isolation du réseau et des versions de Keras), je te propose une solution alternative simple, fiable et 100 % intégrée à Python pour visualiser tes courbes de précision et de perte. »","metadata":{}},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 1 : Entraînement du modèle\n# ==============================================================================\n# Lancement de l'entraînement et enregistrement des métriques dans 'history'\nhistory = model.fit(\n    x=train_data,                  # Données d'entraînement\n    epochs=NUM_EPOCHS,             # Nombre d'époques défini\n    validation_data=val_data,      # Données de validation\n    callbacks=[early_stopping]     # Callbacks (ex: EarlyStopping)\n)\n\nprint(\"\\n--- Entraînement terminé avec succès ! ---\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:05.598418Z","iopub.execute_input":"2026-08-10T19:21:05.599385Z","iopub.status.idle":"2026-08-10T19:21:10.612474Z","shell.execute_reply.started":"2026-08-10T19:21:05.599343Z","shell.execute_reply":"2026-08-10T19:21:10.611749Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 2 : Définition de la fonction de visualisation des courbes\n# ==============================================================================\n\ndef plot_loss_curves(history):\n    \"\"\"\n    Affiche séparément les courbes de perte (loss) et de précision (accuracy)\n    pour les ensembles d'entraînement et de validation.\n    \n    Argument :\n        history : L'objet retourné par la méthode model.fit()\n    \"\"\"\n    # Extraction des métriques de l'historique d'entraînement\n    loss = history.history['loss']\n    val_loss = history.history['val_loss']\n\n    accuracy = history.history['accuracy']\n    val_accuracy = history.history['val_accuracy']\n\n    epochs = range(len(history.history['loss']))\n\n    # Configuration de la figure Matplotlib\n    plt.figure(figsize=(14, 5))\n\n    # 1. Graphique de la Perte (Loss)\n    plt.subplot(1, 2, 1)\n    plt.plot(epochs, loss, label='Perte Entraînement (train_loss)')\n    plt.plot(epochs, val_loss, label='Perte Validation (val_loss)')\n    plt.title('Évolution de la Perte (Loss)')\n    plt.xlabel('Époques')\n    plt.ylabel('Loss')\n    plt.legend()\n    plt.grid(True)\n\n    # 2. Graphique de la Précision (Accuracy)\n    plt.subplot(1, 2, 2)\n    plt.plot(epochs, accuracy, label='Précision Entraînement (train_accuracy)')\n    plt.plot(epochs, val_accuracy, label='Précision Validation (val_accuracy)')\n    plt.title('Évolution de la Précision (Accuracy)')\n    plt.xlabel('Époques')\n    plt.ylabel('Accuracy')\n    plt.legend()\n    plt.grid(True)\n    \n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:10.613529Z","iopub.execute_input":"2026-08-10T19:21:10.614005Z","iopub.status.idle":"2026-08-10T19:21:10.621397Z","shell.execute_reply.started":"2026-08-10T19:21:10.613927Z","shell.execute_reply":"2026-08-10T19:21:10.620657Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 3 : Appel de la fonction de visualisation\n# ==============================================================================\nplot_loss_curves(history)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:10.622452Z","iopub.execute_input":"2026-08-10T19:21:10.623027Z","iopub.status.idle":"2026-08-10T19:21:10.931593Z","shell.execute_reply.started":"2026-08-10T19:21:10.622991Z","shell.execute_reply":"2026-08-10T19:21:10.930912Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Checking the TensorBoard logs\n\nThe TensorBoard magic function (`%tensorboard`) will access the logs directory we created earlier and visualize its contents.","metadata":{"id":"PkL0AsfoLYfw"}},{"cell_type":"code","source":"# %tensorboard --logdir drive/My\\ Drive/Dog\\ Vision/logs","metadata":{"id":"g0j3jq3FFxhL","outputId":"72a8b43f-59e8-43ea-df33-86ec1e95f31c","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:10.932406Z","iopub.execute_input":"2026-08-10T19:21:10.933146Z","iopub.status.idle":"2026-08-10T19:21:10.936763Z","shell.execute_reply.started":"2026-08-10T19:21:10.933120Z","shell.execute_reply":"2026-08-10T19:21:10.936133Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Créer le dossier `logs` dans `/kaggle/working/`","metadata":{}},{"cell_type":"code","source":"import os\n\n# Chemin vers le dossier de logs sur Kaggle\nLOG_DIR = \"/kaggle/working/logs\"\n\n# Vérification du contenu du dossier\nif os.path.exists(LOG_DIR):\n    files = os.listdir(LOG_DIR)\n    print(f\"Dossiers/fichiers trouvés dans {LOG_DIR} : {files}\")\nelse:\n    print(f\"Attention : Le dossier {LOG_DIR} n'existe pas encore. Lane l'entraînement d'abord !\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:10.937684Z","iopub.execute_input":"2026-08-10T19:21:10.937936Z","iopub.status.idle":"2026-08-10T19:21:10.949159Z","shell.execute_reply.started":"2026-08-10T19:21:10.937914Z","shell.execute_reply":"2026-08-10T19:21:10.948440Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ==============================================================================\n# # ÉTAPE 1 : Chargement de l'extension TensorBoard\n# # ==============================================================================\n# %load_ext tensorboard","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:10.949856Z","iopub.execute_input":"2026-08-10T19:21:10.950096Z","iopub.status.idle":"2026-08-10T19:21:10.962919Z","shell.execute_reply.started":"2026-08-10T19:21:10.950075Z","shell.execute_reply":"2026-08-10T19:21:10.962094Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ==============================================================================\n# # ÉTAPE 1 : Rechargement de l'extension TensorBoard (pour vider le cache)\n# # ==============================================================================\n# %reload_ext tensorboard","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:10.963799Z","iopub.execute_input":"2026-08-10T19:21:10.964584Z","iopub.status.idle":"2026-08-10T19:21:10.973502Z","shell.execute_reply.started":"2026-08-10T19:21:10.964547Z","shell.execute_reply":"2026-08-10T19:21:10.972717Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # ==============================================================================\n# # ÉTAPE 2 : Lancement de l'interface graphique TensorBoard\n# # ==============================================================================\n# # Indique à TensorBoard de lire les logs stockés dans '/kaggle/working/logs/'\n# %tensorboard --logdir /kaggle/working/logs/","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:10.974531Z","iopub.execute_input":"2026-08-10T19:21:10.974863Z","iopub.status.idle":"2026-08-10T19:21:10.984162Z","shell.execute_reply.started":"2026-08-10T19:21:10.974841Z","shell.execute_reply":"2026-08-10T19:21:10.983481Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Libération du port bloqué en arrêtant le processus TensorBoard précédent\n# !kill 9283","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T20:03:44.142483Z","iopub.execute_input":"2026-08-10T20:03:44.143134Z","iopub.status.idle":"2026-08-10T20:03:44.146398Z","shell.execute_reply.started":"2026-08-10T20:03:44.143101Z","shell.execute_reply":"2026-08-10T20:03:44.145764Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Détecter automatiquement le dernier dossier de logs\nimport os\n\ndef get_latest_log_dir(base_dir=\"/kaggle/working/logs\"):\n    \"\"\"\n    Trouve et retourne le chemin du sous-dossier de logs le plus récent.\n    \"\"\"\n    if not os.path.exists(base_dir):\n        print(f\"Le dossier racine {base_dir} n'existe pas encore.\")\n        return None\n        \n    # Liste tous les sous-dossiers dans /kaggle/working/logs/\n    subdirs = [os.path.join(base_dir, d) for d in os.listdir(base_dir) \n               if os.path.isdir(os.path.join(base_dir, d))]\n    \n    if not subdirs:\n        print(\"Aucun sous-dossier de logs trouvé.\")\n        return None\n        \n    # Trie les dossiers par date de modification (le plus récent en dernier)\n    latest_subdir = max(subdirs, key=os.path.getmtime)\n    return latest_subdir\n\n# Récupération du chemin le plus récent\nlatest_logs = get_latest_log_dir()\nprint(f\"Dernière expérience détectée : {latest_logs}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:11.173160Z","iopub.execute_input":"2026-08-10T19:21:11.173503Z","iopub.status.idle":"2026-08-10T19:21:11.181063Z","shell.execute_reply.started":"2026-08-10T19:21:11.173473Z","shell.execute_reply":"2026-08-10T19:21:11.180179Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Recharge TensorBoard en ciblant le dernier dossier d'entraînement précis\n%reload_ext tensorboard\n%tensorboard --logdir /kaggle/working/logs/20260810-092005","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:11.182111Z","iopub.execute_input":"2026-08-10T19:21:11.182407Z","iopub.status.idle":"2026-08-10T19:21:16.207033Z","shell.execute_reply.started":"2026-08-10T19:21:11.182386Z","shell.execute_reply":"2026-08-10T19:21:16.206266Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Making and evaluating predictions using a trained model","metadata":{"id":"vro2qwK8NXob"}},{"cell_type":"code","source":"val_data","metadata":{"id":"mIXNUymfNv36","outputId":"25fd7fa0-0b44-4775-ccc1-ecba5af65c53","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:16.207999Z","iopub.execute_input":"2026-08-10T19:21:16.208387Z","iopub.status.idle":"2026-08-10T19:21:16.214129Z","shell.execute_reply.started":"2026-08-10T19:21:16.208362Z","shell.execute_reply":"2026-08-10T19:21:16.213300Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Make predictions on the validation data (not used to train on)\npredictions = model.predict(val_data, verbose=1)\npredictions","metadata":{"id":"nYZFQjleNKYJ","outputId":"5fca9c70-8cf2-49e5-8133-03f1f8beef29","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:16.215044Z","iopub.execute_input":"2026-08-10T19:21:16.215542Z","iopub.status.idle":"2026-08-10T19:21:17.123137Z","shell.execute_reply.started":"2026-08-10T19:21:16.215516Z","shell.execute_reply":"2026-08-10T19:21:17.122424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(predictions[0])","metadata":{"id":"RoVVytwPOaf3","outputId":"85d6642a-00ea-43f5-a285-7394b391af91","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.124070Z","iopub.execute_input":"2026-08-10T19:21:17.124273Z","iopub.status.idle":"2026-08-10T19:21:17.129652Z","shell.execute_reply.started":"2026-08-10T19:21:17.124252Z","shell.execute_reply":"2026-08-10T19:21:17.128910Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.sum(predictions[0])","metadata":{"id":"pUSz35FGOpxY","outputId":"5c029877-d0f1-4b26-e25f-68090e005fb3","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.130582Z","iopub.execute_input":"2026-08-10T19:21:17.131021Z","iopub.status.idle":"2026-08-10T19:21:17.142788Z","shell.execute_reply.started":"2026-08-10T19:21:17.130984Z","shell.execute_reply":"2026-08-10T19:21:17.142021Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.sum(predictions[1])","metadata":{"id":"thX-ZTiIO-bm","outputId":"41693e1a-536a-4dcb-cd9b-aa52df9ee647","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.143627Z","iopub.execute_input":"2026-08-10T19:21:17.143912Z","iopub.status.idle":"2026-08-10T19:21:17.154658Z","shell.execute_reply.started":"2026-08-10T19:21:17.143873Z","shell.execute_reply":"2026-08-10T19:21:17.153699Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions.shape","metadata":{"id":"1MnkPMpiNpmn","outputId":"4f9dcdc7-3612-47e1-8220-1cd341d17192","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.155576Z","iopub.execute_input":"2026-08-10T19:21:17.155918Z","iopub.status.idle":"2026-08-10T19:21:17.166484Z","shell.execute_reply.started":"2026-08-10T19:21:17.155884Z","shell.execute_reply":"2026-08-10T19:21:17.165822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(y_val)","metadata":{"id":"iAX_IVpsOIKP","outputId":"122ac079-218d-48a8-8ba5-e4c37146b5be","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.167309Z","iopub.execute_input":"2026-08-10T19:21:17.167650Z","iopub.status.idle":"2026-08-10T19:21:17.179793Z","shell.execute_reply.started":"2026-08-10T19:21:17.167612Z","shell.execute_reply":"2026-08-10T19:21:17.178801Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(unique_breeds)","metadata":{"id":"5rPrc_EUOLlG","outputId":"3f71049e-9f22-4e23-ccdb-462eb0b8b585","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.180839Z","iopub.execute_input":"2026-08-10T19:21:17.181189Z","iopub.status.idle":"2026-08-10T19:21:17.191450Z","shell.execute_reply.started":"2026-08-10T19:21:17.181157Z","shell.execute_reply":"2026-08-10T19:21:17.190546Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# First prediction\nindex = 42\nprint(predictions[index])\nprint(f\"Max value (probability of prediction): {np.max(predictions[index])}\")\nprint(f\"Sum: {np.sum(predictions[index])}\")\nprint(f\"Max index: {np.argmax(predictions[index])}\")\nprint(f\"Predicted label: {unique_breeds[np.argmax(predictions[index])]}\")","metadata":{"id":"dobaPNK5ORMO","outputId":"9e8b6ce5-c4ca-4bcf-985d-8d404106f2f0","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.192440Z","iopub.execute_input":"2026-08-10T19:21:17.192739Z","iopub.status.idle":"2026-08-10T19:21:17.204414Z","shell.execute_reply.started":"2026-08-10T19:21:17.192701Z","shell.execute_reply":"2026-08-10T19:21:17.203810Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"unique_breeds[113]","metadata":{"id":"3i6Pmb2IQlNo","outputId":"071b4c9d-756b-4545-bc60-6d0d37ebf240","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.205307Z","iopub.execute_input":"2026-08-10T19:21:17.205587Z","iopub.status.idle":"2026-08-10T19:21:17.216331Z","shell.execute_reply.started":"2026-08-10T19:21:17.205556Z","shell.execute_reply":"2026-08-10T19:21:17.215569Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Having the above functionality is great but we want to be able to do it at scale.\n\nAnd it would be even better if we could see the image the prediction is being made on!\n\n**Note:** Prediction probabilities are also known as confidence levels.","metadata":{"id":"6bCzy6VCRctI"}},{"cell_type":"code","source":"# Turn prediction probabilities into their respective label (easier to understand)\ndef get_pred_label(prediction_probabilities):\n    \"\"\"\n    Turn an array of prediction probabilities into a label.\n    \"\"\"\n    return unique_breeds[np.argmax(prediction_probabilities)]\n\n# Get a predicted label based on an array of prediction probabilities\npred_label = get_pred_label(predictions[0])\npred_label","metadata":{"id":"PcgEbBr7Q9Qn","outputId":"34f5f686-d859-4a03-9f73-0f4b704b91c5","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.217263Z","iopub.execute_input":"2026-08-10T19:21:17.217554Z","iopub.status.idle":"2026-08-10T19:21:17.227735Z","shell.execute_reply.started":"2026-08-10T19:21:17.217521Z","shell.execute_reply":"2026-08-10T19:21:17.226902Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now since our validation data is still in a batch dataset, we'll have to unbatchify it to make predictions on the validation images and then compare those predictions to the validation labels (truth labels).","metadata":{"id":"FIObFc9rTMNe"}},{"cell_type":"code","source":"val_data","metadata":{"id":"ShBAoiIPS9ES","outputId":"cd58151f-e218-4225-b513-958867cb51d7","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.228680Z","iopub.execute_input":"2026-08-10T19:21:17.229092Z","iopub.status.idle":"2026-08-10T19:21:17.243168Z","shell.execute_reply.started":"2026-08-10T19:21:17.229057Z","shell.execute_reply":"2026-08-10T19:21:17.242508Z"}},"outputs":[],"execution_count":null},{"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 returns separate arrays of images and labels.\n    \"\"\"\n    images = []\n    labels = []\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    return images, labels\n\n# Unbatchify the validation data\nval_images, val_labels = unbatchify(val_data)\nval_images[0], val_labels[0]","metadata":{"id":"cGNY_1icTif4","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:17.244198Z","iopub.execute_input":"2026-08-10T19:21:17.244510Z","iopub.status.idle":"2026-08-10T19:21:17.999679Z","shell.execute_reply.started":"2026-08-10T19:21:17.244487Z","shell.execute_reply":"2026-08-10T19:21:17.999036Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"get_pred_label(val_labels[0])","metadata":{"id":"eY9YUmX5XfEE","outputId":"ce67bc89-dd4f-413d-cc53-11b9713807f6","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.000653Z","iopub.execute_input":"2026-08-10T19:21:18.000980Z","iopub.status.idle":"2026-08-10T19:21:18.006251Z","shell.execute_reply.started":"2026-08-10T19:21:18.000917Z","shell.execute_reply":"2026-08-10T19:21:18.005582Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images_ = []\nlabels_ = []\n\n# Loop through unbatched data\nfor image, label in val_data.unbatch().as_numpy_iterator():\n    # print(image)\n    # print(label)\n    images_.append(image)\n    labels_.append(label)\n\nimages_[0], labels_[0]","metadata":{"id":"5x0shM7dT-Ng","outputId":"06f72fb7-a232-479e-a23f-0b220373cb44","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.007153Z","iopub.execute_input":"2026-08-10T19:21:18.007519Z","iopub.status.idle":"2026-08-10T19:21:18.303652Z","shell.execute_reply.started":"2026-08-10T19:21:18.007464Z","shell.execute_reply":"2026-08-10T19:21:18.302866Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"get_pred_label(labels_[0])","metadata":{"id":"6-voGi3fUz_1","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.304661Z","iopub.execute_input":"2026-08-10T19:21:18.305055Z","iopub.status.idle":"2026-08-10T19:21:18.310382Z","shell.execute_reply.started":"2026-08-10T19:21:18.305029Z","shell.execute_reply":"2026-08-10T19:21:18.309664Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"get_pred_label(predictions[0])","metadata":{"id":"qQqqxHBVV4UM","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.311387Z","iopub.execute_input":"2026-08-10T19:21:18.311745Z","iopub.status.idle":"2026-08-10T19:21:18.321758Z","shell.execute_reply.started":"2026-08-10T19:21:18.311707Z","shell.execute_reply":"2026-08-10T19:21:18.321012Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now we've got ways to get:\n* Prediction labels\n* Validation labels (truth labels)\n* Validatin images\n\nLet's make some function to make these all a bit more visualize.\n\nWe'll create a function which:\n* Takes an array of prediction probabilities, an array of truth labels and an array of images and an integer. ✅\n* Convert the prediction probabilities to a predicted label. ✅\n* Plot the predicted label, its predicted probability, the truth label and the target image on a single plot. ✅","metadata":{"id":"mMtXpYx6YCtr"}},{"cell_type":"code","source":"def plot_pred(prediction_probabilities, labels, images, n=1):\n    \"\"\"\n    View the prediction, ground truth and images for sample n\n    \"\"\"\n    pred_prob, true_label, image = prediction_probabilities[n], labels[n], images[n]\n\n    # Get the pred label\n    pred_label = get_pred_label(pred_prob)\n\n    # Plot image & remove ticks\n    plt.imshow(image)\n    plt.xticks([])\n    plt.yticks([])\n\n    # Change the colour of the title depending on if the prediction is right or wrong\n    if pred_label == true_label:\n        color = \"green\"\n    else:\n        color = \"red\"\n\n    # Change plot title to be predicted, probability of prediction and truth label\n    plt.title(\"{} {:2.0f}% {}\".format(pred_label,\n                                      np.max(pred_prob)*100,\n                                      true_label),\n                                      color=color)","metadata":{"id":"bUwYR6rtYa_z","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.323063Z","iopub.execute_input":"2026-08-10T19:21:18.323345Z","iopub.status.idle":"2026-08-10T19:21:18.333808Z","shell.execute_reply.started":"2026-08-10T19:21:18.323321Z","shell.execute_reply":"2026-08-10T19:21:18.333159Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_pred(prediction_probabilities=predictions,\n          labels=val_labels,\n          images=val_images,\n          n=77)","metadata":{"id":"qEt8VGq8ZdhK","outputId":"f8744dd6-261a-403c-b8f8-fb15ddfa937d","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.334814Z","iopub.execute_input":"2026-08-10T19:21:18.335301Z","iopub.status.idle":"2026-08-10T19:21:18.470307Z","shell.execute_reply.started":"2026-08-10T19:21:18.335271Z","shell.execute_reply":"2026-08-10T19:21:18.469635Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now we've got one function to visualize our models top prediction, let's make another to view our models top 10 predictions.\n\nThis function will:\n* Take an input of prediction probabilities array and a ground truth array and an integer ✅\n* Find the prediction using `get_pred_label()` ✅\n* Find the top 10:\n    * Prediction probabilities indexes ✅\n    * prediction probabilities values ✅\n    * Prediction labels ✅\n* Plot the top 10 prediction probability values and labels, coloring the true label green ✅","metadata":{"id":"BkIwX63Md2pI"}},{"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    # Setup plot\n    top_plot = plt.bar(np.arange(len(top_10_pred_labels)),\n                       top_10_pred_values,\n                       color=\"grey\")\n    plt.xticks(np.arange(len(top_10_pred_labels)),\n               labels=top_10_pred_labels,\n               rotation=\"vertical\")\n\n    # Change color of true label\n    if np.isin(true_label, top_10_pred_labels):\n        top_plot[np.argmax(top_10_pred_labels == true_label)].set_color(\"green\")\n    else:\n        pass","metadata":{"id":"L5MTgxw1bgRj","trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.471277Z","iopub.execute_input":"2026-08-10T19:21:18.472029Z","iopub.status.idle":"2026-08-10T19:21:18.477338Z","shell.execute_reply.started":"2026-08-10T19:21:18.471992Z","shell.execute_reply":"2026-08-10T19:21:18.476742Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_pred_conf(prediction_probabilities=predictions,\n               labels=val_labels,\n               n=9)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.478411Z","iopub.execute_input":"2026-08-10T19:21:18.478748Z","iopub.status.idle":"2026-08-10T19:21:18.641202Z","shell.execute_reply.started":"2026-08-10T19:21:18.478717Z","shell.execute_reply":"2026-08-10T19:21:18.640547Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions[0].argsort()[-10:]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.642208Z","iopub.execute_input":"2026-08-10T19:21:18.642503Z","iopub.status.idle":"2026-08-10T19:21:18.647750Z","shell.execute_reply.started":"2026-08-10T19:21:18.642458Z","shell.execute_reply":"2026-08-10T19:21:18.647220Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions[0].argsort()[10:][::-1]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.648889Z","iopub.execute_input":"2026-08-10T19:21:18.649266Z","iopub.status.idle":"2026-08-10T19:21:18.661365Z","shell.execute_reply.started":"2026-08-10T19:21:18.649242Z","shell.execute_reply":"2026-08-10T19:21:18.660744Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions[0][predictions[0].argsort()[-10:][::-1]]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.662360Z","iopub.execute_input":"2026-08-10T19:21:18.662717Z","iopub.status.idle":"2026-08-10T19:21:18.672517Z","shell.execute_reply.started":"2026-08-10T19:21:18.662652Z","shell.execute_reply":"2026-08-10T19:21:18.671809Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions[0].max()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.673383Z","iopub.execute_input":"2026-08-10T19:21:18.673671Z","iopub.status.idle":"2026-08-10T19:21:18.684131Z","shell.execute_reply.started":"2026-08-10T19:21:18.673643Z","shell.execute_reply":"2026-08-10T19:21:18.683416Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"unique_breeds[predictions[0].argsort()[-10:][::-1]]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.685290Z","iopub.execute_input":"2026-08-10T19:21:18.685579Z","iopub.status.idle":"2026-08-10T19:21:18.696485Z","shell.execute_reply.started":"2026-08-10T19:21:18.685550Z","shell.execute_reply":"2026-08-10T19:21:18.695690Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now we've got some function to help us visualize our predictions and evaluate our modle, let's check out a few.","metadata":{}},{"cell_type":"code","source":"# Let's check out a few predictions and their different values\ni_multiplier = 20\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=predictions,\n              labels=val_labels,\n              images=val_images,\n              n=i+i_multiplier)\n    plt.subplot(num_rows, 2*num_cols, 2*i+2)\n    plot_pred_conf(prediction_probabilities=predictions,\n                   labels=val_labels,\n                   n=i+i_multiplier)\n\nplt.tight_layout(h_pad=1.0)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:18.697429Z","iopub.execute_input":"2026-08-10T19:21:18.697819Z","iopub.status.idle":"2026-08-10T19:21:19.853512Z","shell.execute_reply.started":"2026-08-10T19:21:18.697795Z","shell.execute_reply":"2026-08-10T19:21:19.852395Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Challenge:** How would you create a confusion matrix with our models predictions and true labels?","metadata":{}},{"cell_type":"markdown","source":"## Saving and reloading a trained model","metadata":{}},{"cell_type":"markdown","source":"### Créer le dossier `models` dans l'espace de travai Kaggle","metadata":{}},{"cell_type":"code","source":"import os\n\n# 1. Définition du chemin du dossier de models dans l'espace de travail Kaggle\nMODEL_DIR = \"/kaggle/working/models/\"\n\n# 2. Création automatique du dossier s'il n'existe pas encore\nos.makedirs(MODEL_DIR, exist_ok=True)\n\n# 3. Message de confirmation\nprint(f\"Le dossier de models est prêt à l'emplacement : {MODEL_DIR}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:38:46.043469Z","iopub.execute_input":"2026-08-10T19:38:46.044051Z","iopub.status.idle":"2026-08-10T19:38:46.047846Z","shell.execute_reply.started":"2026-08-10T19:38:46.044013Z","shell.execute_reply":"2026-08-10T19:38:46.047001Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a function to save a model\ndef save_model(model, suffix=None):\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    modeldir = os.path.join(\"/kaggle/working/models\",\n                            datetime.datetime.now().strftime(\"%Y%m%d-%H%M%s\"))\n    model_path = modeldir + \"-\" + suffix + \".h5\"  # save format of model\n    print(f\"Saving model to: {model_path}...\")\n    model.save(model_path)\n    return model_path","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:19.860783Z","iopub.execute_input":"2026-08-10T19:21:19.861219Z","iopub.status.idle":"2026-08-10T19:21:19.872194Z","shell.execute_reply.started":"2026-08-10T19:21:19.861170Z","shell.execute_reply":"2026-08-10T19:21:19.871437Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a function to load a trained model\ndef load_model(model_path):\n    \"\"\"\n    Loads a saved model from a specified path.\n    \"\"\"\n    print(f\"Loading saved model from: {model_path}\")\n    model = tf_keras.models.load_model(model_path,\n                                       custom_objects={\"KerasLayer\":hub.KerasLayer})\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:19.873332Z","iopub.execute_input":"2026-08-10T19:21:19.873709Z","iopub.status.idle":"2026-08-10T19:21:19.884205Z","shell.execute_reply.started":"2026-08-10T19:21:19.873677Z","shell.execute_reply":"2026-08-10T19:21:19.883480Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now we've got functions to save and load a trained model, let's make sure they work!","metadata":{}},{"cell_type":"code","source":"# Save our model trained on 1000 images \nsave_model(model, suffix=\"1000-images-mobilenetv2-Adam\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:21:19.885164Z","iopub.execute_input":"2026-08-10T19:21:19.885822Z","iopub.status.idle":"2026-08-10T19:21:20.144354Z","shell.execute_reply.started":"2026-08-10T19:21:19.885783Z","shell.execute_reply":"2026-08-10T19:21:20.143671Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load a trained model\nloaded_1000_image_model = load_model('/kaggle/working/models/20260810-19211786389679-1000-images-mobilenetv2-Adam.h5')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:23:53.418734Z","iopub.execute_input":"2026-08-10T19:23:53.419733Z","iopub.status.idle":"2026-08-10T19:23:56.042703Z","shell.execute_reply.started":"2026-08-10T19:23:53.419688Z","shell.execute_reply":"2026-08-10T19:23:56.042059Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Evaluate the pre-saved model\nmodel.evaluate(val_data)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:01.642796Z","iopub.execute_input":"2026-08-10T19:24:01.643249Z","iopub.status.idle":"2026-08-10T19:24:02.088347Z","shell.execute_reply.started":"2026-08-10T19:24:01.643218Z","shell.execute_reply":"2026-08-10T19:24:02.087480Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Evaluate the loaded model\nloaded_1000_image_model.evaluate(val_data)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:03.600598Z","iopub.execute_input":"2026-08-10T19:24:03.601057Z","iopub.status.idle":"2026-08-10T19:24:04.508126Z","shell.execute_reply.started":"2026-08-10T19:24:03.601027Z","shell.execute_reply":"2026-08-10T19:24:04.507226Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training a big dog model (on the full data)","metadata":{}},{"cell_type":"code","source":"len(X), len(y)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:07.576892Z","iopub.execute_input":"2026-08-10T19:24:07.577441Z","iopub.status.idle":"2026-08-10T19:24:07.582432Z","shell.execute_reply.started":"2026-08-10T19:24:07.577407Z","shell.execute_reply":"2026-08-10T19:24:07.581723Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X[:10]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:08.073261Z","iopub.execute_input":"2026-08-10T19:24:08.073687Z","iopub.status.idle":"2026-08-10T19:24:08.079180Z","shell.execute_reply.started":"2026-08-10T19:24:08.073658Z","shell.execute_reply":"2026-08-10T19:24:08.078334Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train[:10]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:08.645526Z","iopub.execute_input":"2026-08-10T19:24:08.646277Z","iopub.status.idle":"2026-08-10T19:24:08.651667Z","shell.execute_reply.started":"2026-08-10T19:24:08.646246Z","shell.execute_reply":"2026-08-10T19:24:08.650751Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y[:2]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:11.945589Z","iopub.execute_input":"2026-08-10T19:24:11.946318Z","iopub.status.idle":"2026-08-10T19:24:11.951297Z","shell.execute_reply.started":"2026-08-10T19:24:11.946289Z","shell.execute_reply":"2026-08-10T19:24:11.950660Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a data batch with the full data set\nfull_data = create_data_batches(X, y)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:12.548597Z","iopub.execute_input":"2026-08-10T19:24:12.549044Z","iopub.status.idle":"2026-08-10T19:24:12.627843Z","shell.execute_reply.started":"2026-08-10T19:24:12.549014Z","shell.execute_reply":"2026-08-10T19:24:12.627196Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"full_data","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:16.720506Z","iopub.execute_input":"2026-08-10T19:24:16.721060Z","iopub.status.idle":"2026-08-10T19:24:16.725998Z","shell.execute_reply.started":"2026-08-10T19:24:16.721030Z","shell.execute_reply":"2026-08-10T19:24:16.725319Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a model for full model\nfull_model = create_model()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:18.416924Z","iopub.execute_input":"2026-08-10T19:24:18.417292Z","iopub.status.idle":"2026-08-10T19:24:21.151022Z","shell.execute_reply.started":"2026-08-10T19:24:18.417266Z","shell.execute_reply":"2026-08-10T19:24:21.150114Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create full model callbacks\nfull_model_tensorboard = create_tensorboard_callback()\n# No validation set when training on all the data, so we can't monitor validation accuracy\nfull_model_early_stopping = tf.keras.callbacks.EarlyStopping(monitor=\"accuracy\",\n                                                             patience=3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:24:21.152366Z","iopub.execute_input":"2026-08-10T19:24:21.152680Z","iopub.status.idle":"2026-08-10T19:24:21.157282Z","shell.execute_reply.started":"2026-08-10T19:24:21.152646Z","shell.execute_reply":"2026-08-10T19:24:21.156630Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Note:** Running the cell below will take a little while (maybe up to 30 minutes for the first epoch) because the GPU we're using in the runtime has to load all of the images into memory.","metadata":{}},{"cell_type":"code","source":"# Fit the full model to the full data\nfull_model.fit(x=full_data,\n               epochs=NUM_EPOCHS,\n               callbacks=[full_model_tensorboard, full_model_early_stopping])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:25:26.967754Z","iopub.execute_input":"2026-08-10T19:25:26.968504Z","iopub.status.idle":"2026-08-10T19:26:15.521824Z","shell.execute_reply.started":"2026-08-10T19:25:26.968471Z","shell.execute_reply":"2026-08-10T19:26:15.521148Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"save_model(full_model, suffix=\"full-image-set-mobilenetv2-Adam\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:28:39.516695Z","iopub.execute_input":"2026-08-10T19:28:39.517168Z","iopub.status.idle":"2026-08-10T19:28:39.753132Z","shell.execute_reply.started":"2026-08-10T19:28:39.517138Z","shell.execute_reply":"2026-08-10T19:28:39.752473Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load in the full model\nloaded_full_model = load_model('/kaggle/working/models/20260810-19281786390119-full-image-set-mobilenetv2-Adam.h5')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:29:01.099444Z","iopub.execute_input":"2026-08-10T19:29:01.100245Z","iopub.status.idle":"2026-08-10T19:29:03.308637Z","shell.execute_reply.started":"2026-08-10T19:29:01.100215Z","shell.execute_reply":"2026-08-10T19:29:03.308015Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Making predictions on the test dataset\n\nSince our model has been trained on images in the form of Tensor batches, to make predictions on the test data, we'll have to get it into the same format.\n\nLuckly we create `create_data_batches()` earlier which can take a list of filenames as input and conver them into Tensor batches.\n\nTo make predictions on the test data, we'll:\n* [X] Get the test image filenames\n* [X] Convert the filenames into test data batches using `create_data_batches()` and setting the `test_data` parameter to `True` (since the test data doesn't have labels).\n* Make a predictions array by passing the test batches to the `predict()` method called on our model.","metadata":{}},{"cell_type":"code","source":"# Load test image filenames\ntest_path = \"/kaggle/input/competitions/dog-breed-identification/test/\"\ntest_filenames = [test_path + fname for fname in os.listdir(test_path)]\ntest_filenames[:10]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:29:07.958266Z","iopub.execute_input":"2026-08-10T19:29:07.958793Z","iopub.status.idle":"2026-08-10T19:29:08.162259Z","shell.execute_reply.started":"2026-08-10T19:29:07.958762Z","shell.execute_reply":"2026-08-10T19:29:08.161626Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(test_filenames)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:29:08.651688Z","iopub.execute_input":"2026-08-10T19:29:08.651905Z","iopub.status.idle":"2026-08-10T19:29:08.657165Z","shell.execute_reply.started":"2026-08-10T19:29:08.651885Z","shell.execute_reply":"2026-08-10T19:29:08.656540Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create test data batch\ntest_data = create_data_batches(test_filenames, test_data=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:30:36.487843Z","iopub.execute_input":"2026-08-10T19:30:36.488714Z","iopub.status.idle":"2026-08-10T19:30:36.511109Z","shell.execute_reply.started":"2026-08-10T19:30:36.488681Z","shell.execute_reply":"2026-08-10T19:30:36.510255Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_data","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:30:38.302588Z","iopub.execute_input":"2026-08-10T19:30:38.303099Z","iopub.status.idle":"2026-08-10T19:30:38.308193Z","shell.execute_reply.started":"2026-08-10T19:30:38.303068Z","shell.execute_reply":"2026-08-10T19:30:38.307399Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Création du dossier 'Dog_vision' dans l'espace de travail Kaggle","metadata":{}},{"cell_type":"code","source":"# ==============================================================================\n# ÉTAPE 1 : Création du dossier 'Dog_vision' dans l'espace de travail Kaggle\n# ==============================================================================\n\n# 1. Définition du chemin du dossier (utilisation du tirage bas _ pour éviter les bugs d'espace)\nDIRECTORY_PATH = \"/kaggle/working/Dog_vision/\"\n\n# 2. Création automatique du répertoire s'il n'existe pas encore\nos.makedirs(DIRECTORY_PATH, exist_ok=True)\n\n# 3. Message de confirmation\nprint(f\"Dossier prêt à l'emplacement : {DIRECTORY_PATH}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:49:26.652624Z","iopub.execute_input":"2026-08-10T19:49:26.653472Z","iopub.status.idle":"2026-08-10T19:49:26.659100Z","shell.execute_reply.started":"2026-08-10T19:49:26.653442Z","shell.execute_reply":"2026-08-10T19:49:26.658030Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Note:** Calling `predict()` on our full model and passing it the test data batch will take a long time to run (avout an ~1hr).","metadata":{}},{"cell_type":"code","source":"# Make predictions on test data batch using the loaded full model\ntest_predictions = loaded_full_model.predict(test_data,\n                                             verbose=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:34:25.591839Z","iopub.execute_input":"2026-08-10T19:34:25.592890Z","iopub.status.idle":"2026-08-10T19:34:54.213583Z","shell.execute_reply.started":"2026-08-10T19:34:25.592855Z","shell.execute_reply":"2026-08-10T19:34:54.212892Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.savetxt(\"/kaggle/working/Dog_vision/preds_array.csv\", test_predictions, delimiter=\",\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:53:02.892303Z","iopub.execute_input":"2026-08-10T19:53:02.892891Z","iopub.status.idle":"2026-08-10T19:53:03.955298Z","shell.execute_reply.started":"2026-08-10T19:53:02.892860Z","shell.execute_reply":"2026-08-10T19:53:03.954631Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load predictions (NumPy array) from csv file\ntest_predictions = np.loadtxt(\"/kaggle/working/Dog_vision/preds_array.csv\", delimiter=\",\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:53:45.074803Z","iopub.execute_input":"2026-08-10T19:53:45.075338Z","iopub.status.idle":"2026-08-10T19:53:45.570756Z","shell.execute_reply.started":"2026-08-10T19:53:45.075308Z","shell.execute_reply":"2026-08-10T19:53:45.569820Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_predictions[:10]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:54:18.270206Z","iopub.execute_input":"2026-08-10T19:54:18.270888Z","iopub.status.idle":"2026-08-10T19:54:18.276675Z","shell.execute_reply.started":"2026-08-10T19:54:18.270856Z","shell.execute_reply":"2026-08-10T19:54:18.275899Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_predictions.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T19:54:37.880712Z","iopub.execute_input":"2026-08-10T19:54:37.881618Z","iopub.status.idle":"2026-08-10T19:54:37.887828Z","shell.execute_reply.started":"2026-08-10T19:54:37.881586Z","shell.execute_reply":"2026-08-10T19:54:37.887024Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Preparing test dataset predictions for Kaggle\n\nLooking at the Kaggle sample submission, we find that it wants our models prediction probability outputs in a DataFrame with an ID and a column for each different dog breed.\n<www.kaggle.com/competitions/dog-breed-identification/overview/evaluation>\n\nTo get the data in this format, we'll:\n* [X] Create a pandas DataFrame with an ID column as well as a column for each dog breed.\n* Add data to the ID column by extracting the test image ID's from their filepaths.\n* Add data (the prediction probabilities) to each of the dog breed columns.\n* Export the DataFrame as a CSV to submit it to Kaggle.","metadata":{}},{"cell_type":"code","source":"# [\"id\"] + list(unique_breeds)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T20:13:04.409076Z","iopub.execute_input":"2026-08-10T20:13:04.409492Z","iopub.status.idle":"2026-08-10T20:13:04.413320Z","shell.execute_reply.started":"2026-08-10T20:13:04.409462Z","shell.execute_reply":"2026-08-10T20:13:04.412576Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a pandas DataFrame with empty columns\npreds_df = pd.DataFrame(columns=[\"id\"] + list(unique_breeds))\npreds_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T20:14:27.731502Z","iopub.execute_input":"2026-08-10T20:14:27.732288Z","iopub.status.idle":"2026-08-10T20:14:27.746470Z","shell.execute_reply.started":"2026-08-10T20:14:27.732258Z","shell.execute_reply":"2026-08-10T20:14:27.745797Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# test_filenames","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T20:15:54.503766Z","iopub.execute_input":"2026-08-10T20:15:54.504427Z","iopub.status.idle":"2026-08-10T20:15:54.507919Z","shell.execute_reply.started":"2026-08-10T20:15:54.504381Z","shell.execute_reply":"2026-08-10T20:15:54.507091Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Append test image ID's to predictions DataFrame\ntest_ids = [os.path.splitext(path)[0] for path in os.listdir(test_path)]\npreds_df[\"id\"] = test_ids","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T20:19:53.446835Z","iopub.execute_input":"2026-08-10T20:19:53.447281Z","iopub.status.idle":"2026-08-10T20:19:53.478642Z","shell.execute_reply.started":"2026-08-10T20:19:53.447250Z","shell.execute_reply":"2026-08-10T20:19:53.477999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# os.path.splitext(test_filenames[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T20:17:59.220294Z","iopub.execute_input":"2026-08-10T20:17:59.220714Z","iopub.status.idle":"2026-08-10T20:17:59.226062Z","shell.execute_reply.started":"2026-08-10T20:17:59.220685Z","shell.execute_reply":"2026-08-10T20:17:59.225277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"preds_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T20:20:24.452226Z","iopub.execute_input":"2026-08-10T20:20:24.452502Z","iopub.status.idle":"2026-08-10T20:20:24.468771Z","shell.execute_reply.started":"2026-08-10T20:20:24.452479Z","shell.execute_reply":"2026-08-10T20:20:24.467880Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Add the prediction probabilities to each dog breed column\npreds_df[list(unique_breeds)] = test_predictions\npreds_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T20:22:11.865380Z","iopub.execute_input":"2026-08-10T20:22:11.865954Z","iopub.status.idle":"2026-08-10T20:22:11.912615Z","shell.execute_reply.started":"2026-08-10T20:22:11.865913Z","shell.execute_reply":"2026-08-10T20:22:11.911763Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Save our predictions dataframe to CSV for submission to Kaggle\npreds_df.to_csv(\"/kaggle/working/Dog_vision/full_model_predictions_submission_1_mobilenetV2.csv\",\n                index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T20:26:27.593141Z","iopub.execute_input":"2026-08-10T20:26:27.593633Z","iopub.status.idle":"2026-08-10T20:26:29.602415Z","shell.execute_reply.started":"2026-08-10T20:26:27.593601Z","shell.execute_reply":"2026-08-10T20:26:29.601503Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Making predictions on custom images\n\nTo make predictions on custom images, we'll:\n* Get the filepaths of our own images.\n* Turn the filepaths into data batches using `create_data_batches()`. And since our custom images won't have labels, we set the `test_data` parameter to `True`.\n* Pass the custom image data batch to our model's `predict()` method.\n* Convert the prediction output probabilities to predictions labels.\n* Compare the predicted labels to the custom images.","metadata":{}},{"cell_type":"code","source":"# Get custom image filepaths\ncustom_path = \"/kaggle/input/datasets/elisenai/my-dog-photos/dog-photos/\"\ncustom_image_paths = [custom_path + fname for fname in os.listdir(custom_path)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T21:14:39.795534Z","iopub.execute_input":"2026-08-10T21:14:39.796369Z","iopub.status.idle":"2026-08-10T21:14:39.801439Z","shell.execute_reply.started":"2026-08-10T21:14:39.796338Z","shell.execute_reply":"2026-08-10T21:14:39.800635Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"custom_image_paths","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T21:14:41.756096Z","iopub.execute_input":"2026-08-10T21:14:41.756737Z","iopub.status.idle":"2026-08-10T21:14:41.762117Z","shell.execute_reply.started":"2026-08-10T21:14:41.756708Z","shell.execute_reply":"2026-08-10T21:14:41.761365Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Turn custom images into batch datasets\ncustom_data = create_data_batches(custom_image_paths, test_data=True)\ncustom_data","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T21:16:38.304632Z","iopub.execute_input":"2026-08-10T21:16:38.305580Z","iopub.status.idle":"2026-08-10T21:16:38.329318Z","shell.execute_reply.started":"2026-08-10T21:16:38.305547Z","shell.execute_reply":"2026-08-10T21:16:38.328494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Make predictions on the custom data\ncustom_preds = loaded_full_model.predict(custom_data)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T21:17:52.473266Z","iopub.execute_input":"2026-08-10T21:17:52.473542Z","iopub.status.idle":"2026-08-10T21:17:52.979418Z","shell.execute_reply.started":"2026-08-10T21:17:52.473519Z","shell.execute_reply":"2026-08-10T21:17:52.978523Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"custom_preds.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T21:18:18.335821Z","iopub.execute_input":"2026-08-10T21:18:18.336276Z","iopub.status.idle":"2026-08-10T21:18:18.341624Z","shell.execute_reply.started":"2026-08-10T21:18:18.336245Z","shell.execute_reply":"2026-08-10T21:18:18.340983Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Get custom image prediction labels\ncustom_pred_labels = [get_pred_label(custom_preds[i]) for i in range(len(custom_preds))]\ncustom_pred_labels","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T21:20:02.022421Z","iopub.execute_input":"2026-08-10T21:20:02.023390Z","iopub.status.idle":"2026-08-10T21:20:02.029863Z","shell.execute_reply.started":"2026-08-10T21:20:02.023353Z","shell.execute_reply":"2026-08-10T21:20:02.028918Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Get custom images (our unbatchify() function won't work since there aren't labels... maybe we could fix this later)\ncustom_images = []\n# Loop through unbatched data\nfor image in custom_data.unbatch().as_numpy_iterator():\n    custom_images.append(image)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T21:23:20.950364Z","iopub.execute_input":"2026-08-10T21:23:20.951099Z","iopub.status.idle":"2026-08-10T21:23:21.035383Z","shell.execute_reply.started":"2026-08-10T21:23:20.951066Z","shell.execute_reply":"2026-08-10T21:23:21.034728Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check custom image predictions\nplt.figure(figsize=(10, 10))\nfor i, image in enumerate(custom_images):\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":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-10T21:26:35.085873Z","iopub.execute_input":"2026-08-10T21:26:35.086322Z","iopub.status.idle":"2026-08-10T21:26:35.289312Z","shell.execute_reply.started":"2026-08-10T21:26:35.086292Z","shell.execute_reply":"2026-08-10T21:26:35.288494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}