{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"datasetVersion","sourceId":30378,"datasetId":23777,"databundleVersionId":30535,"isSourceIdPinned":false},{"sourceType":"datasetVersion","sourceId":1455620,"datasetId":853363,"databundleVersionId":1489215}],"dockerImageVersionId":31286,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Import Libraries","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport kagglehub as kh\n\nimport tensorflow as tf\n\nfrom tensorflow import data as tfd\nfrom tensorflow import keras\nfrom keras import layers\nfrom keras import layers\nfrom keras import regularizers\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Activation, Dropout, Flatten, Dense\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D\nfrom tensorflow.keras.utils import image_dataset_from_directory\nfrom tensorflow.keras.preprocessing.image import load_img\nfrom tensorflow.keras.preprocessing import image_dataset_from_directory\nfrom tensorflow.keras.callbacks import ReduceLROnPlateau,EarlyStopping\nfrom tensorflow.keras.saving import load_model\n\nimport warnings\nwarnings.filterwarnings('ignore')\nTF_CPP_MIN_LOG_LEVEL=3\n\nimport os\nimport matplotlib.image as mpimg\nimport seaborn as sns\nfrom sklearn.metrics import classification_report,confusion_matrix,ConfusionMatrixDisplay\n\nprint(\"TensorFlow Version: \", tf.version)\nprint(\"Keras Version: \", keras.version)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-03-25T09:35:29.450486Z","iopub.execute_input":"2026-03-25T09:35:29.451124Z","iopub.status.idle":"2026-03-25T09:35:29.458008Z","shell.execute_reply.started":"2026-03-25T09:35:29.451096Z","shell.execute_reply":"2026-03-25T09:35:29.457279Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Import Dataset\nImport and set the paths for the dataset.","metadata":{}},{"cell_type":"code","source":"# Download latest version of cat & dog dataset\npath = kh.dataset_download(\"tongpython/cat-and-dog\")\npath = kh.dataset_download(\"kunalgupta2616/dog-vs-cat-images-data\")\n\n# Set the directories for test and training data\nbaseDir = '/kaggle/input/datasets/kunalgupta2616/dog-vs-cat-images-data/dogcat/'\ntrainingDir = os.path.join(baseDir, 'train/')\nvalidationDir = os.path.join(baseDir, 'validation/')\ntestDir = '/kaggle/input/datasets/tongpython/cat-and-dog/test_set/test_set'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T09:35:29.459874Z","iopub.execute_input":"2026-03-25T09:35:29.460442Z","iopub.status.idle":"2026-03-25T09:35:29.733556Z","shell.execute_reply.started":"2026-03-25T09:35:29.460421Z","shell.execute_reply":"2026-03-25T09:35:29.732997Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Visualization of the Data\n","metadata":{}},{"cell_type":"code","source":"# Create a figure and set its size to 16in x 16in\nfigure = plt.gcf()\nfigure.set_size_inches(16, 16)\n\n# Get the directories to cat and dog images from our training dataset\ncatDir = os.path.join(trainingDir, 'cats')\ndogDir = os.path.join(trainingDir, 'dogs')\n# Get the file names from those directories\ncatNames = os.listdir(catDir)\ndogNames = os.listdir(dogDir)\n\n# Pick an index to grab images from, I went with 964 but any number less than the number of images will work\npictureIndex = 964\n\n# Get 8 images of cats and dogs using the index above\ncatImages = [os.path.join(catDir, fileName)\n              for fileName in catNames[pictureIndex-8:pictureIndex]]\ndogImages = [os.path.join(dogDir, fileName)\n              for fileName in dogNames[pictureIndex-8:pictureIndex]]\n\nfor i, imagePath in enumerate(catImages + dogImages):\n    sp = plt.subplot(4, 4, i+1)\n    sp.axis('Off')\n\n    img = mpimg.imread(imagePath)\n    plt.imshow(img)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T09:35:29.734315Z","iopub.execute_input":"2026-03-25T09:35:29.734538Z","iopub.status.idle":"2026-03-25T09:35:30.692190Z","shell.execute_reply.started":"2026-03-25T09:35:29.734508Z","shell.execute_reply":"2026-03-25T09:35:30.691217Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Data Preprocessing\nPrepare data for use in training and validation.","metadata":{}},{"cell_type":"code","source":"# Training Hyperparameters\nbatchSize = 128\nimageSize = 200\nepochs = 25\n\n# Check the data for badly encoded images\nskipped = 0\nfor directory in (testDir, validationDir):\n    for petType in (\"cats\", \"dogs\"):\n        folderPath = os.path.join(directory, petType)\n        for fileName in os.listdir(folderPath):\n            filePath = os.path.join(folderPath, fileName)\n            try:\n                fileObj = open(filePath, \"rb\")\n                isJFIF = b\"JFIF\" in fileObj.peek(10)\n            finally:\n                fileObj.close()\n    \n            if not isJFIF:\n                skipped += 1\n\nprint(f\"Detected {skipped} badly formatted images.\")\n\n# Create the datasets using the above hyperparameters\ntrainingDataset = image_dataset_from_directory(trainingDir,\n                                                  image_size = (imageSize, imageSize),\n                                                  seed = 1,\n                                                  batch_size = batchSize,\n                                                  labels = \"inferred\",\n                                                  label_mode = \"binary\")\n\nvalidationDataset = image_dataset_from_directory(validationDir,\n                                                  image_size = (imageSize,imageSize),\n                                                  seed = 1,\n                                                  batch_size = batchSize,\n                                                  labels = \"inferred\",\n                                                  label_mode = \"binary\")\n\ntestDataset = image_dataset_from_directory(testDir,\n                                                  image_size = (imageSize,imageSize),\n                                                  seed = 1,\n                                                  batch_size = batchSize,\n                                                  shuffle=False,\n                                                  labels = \"inferred\",\n                                                  label_mode = \"binary\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T09:35:30.694766Z","iopub.execute_input":"2026-03-25T09:35:30.695178Z","iopub.status.idle":"2026-03-25T09:36:07.638372Z","shell.execute_reply.started":"2026-03-25T09:35:30.695146Z","shell.execute_reply":"2026-03-25T09:36:07.637800Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Class Distribution Visualization","metadata":{}},{"cell_type":"code","source":"graphLabels = ['Cat','Dog']\n\n# Iterate over datasets to get labels\ntrainingLabels = []\nvalidationLabels = []\nfor _, labels in trainingDataset:\n    trainingLabels.append(labels.numpy())\n\nfor _, labels in validationDataset:\n    validationLabels.append(labels.numpy())\n\n# Make the axis 0\ntrainingLabels = np.concatenate(trainingLabels, axis=0)\nvalidationLabels = np.concatenate(validationLabels, axis=0)\n\n_, countTraining = np.unique(trainingLabels,return_counts=True)\n_, countValidation = np.unique(validationLabels,return_counts=True)\n\ntestDF = pd.DataFrame(data = countTraining,index = graphLabels,columns = ['countTraining'])\nvalidationDF = pd.DataFrame(data = countValidation,index = graphLabels,columns = ['countValidation'])\n\nplt.figure(figsize=(20,6),dpi=200)\nsns.set_style('darkgrid')\n\nplt.subplot(131)\nsns.barplot(data=testDF,x=testDF.index,y='countTraining',palette='icefire',width=0.2).set_title('Class Distribution in Training Data',fontsize=13)\nplt.xlabel('Labels',fontsize=10)\nplt.ylabel('Count',fontsize=10)\n\nplt.subplot(132)\nsns.barplot(data=validationDF,x=validationDF.index,y='countValidation',palette='icefire',width=0.2).set_title('Class Distribution in Validation Data',fontsize=13)\nplt.xlabel('Labels',fontsize=10)\nplt.ylabel('Count',fontsize=10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T09:36:07.639385Z","iopub.execute_input":"2026-03-25T09:36:07.639660Z","iopub.status.idle":"2026-03-25T09:36:30.468755Z","shell.execute_reply.started":"2026-03-25T09:36:07.639638Z","shell.execute_reply":"2026-03-25T09:36:30.467947Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Data Augmentation","metadata":{}},{"cell_type":"code","source":"# Data augmentation to reduce overfitting and increase training efficacy by adding depth to the training data\ndataAugmentationLayers = [\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.1),\n    layers.RandomZoom(0.1)\n]\n\ndef data_augmentation(images):\n    for layer in dataAugmentationLayers:\n        images = layer(images)\n    return images\n\n# Rescale the images to 0 to 1 on RBG instead of 1 to 255\nrescalingLayer = [\n    layers.Rescaling(1./255)\n]\n\ndef data_rescaling(images):\n    for layer in rescalingLayer:\n        images = layer(images)\n    return images\n\n# Visualize the data augmentation we will be doing\nplt.figure(figsize=(10, 10))\nfor images, _ in trainingDataset.take(1):\n    for i in range(9):\n        augmentedImages = data_augmentation(images)\n        ax = plt.subplot(3, 3, i + 1)\n        plt.imshow(np.array(augmentedImages[0]).astype(\"uint8\"))\n        plt.axis(\"off\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T09:36:30.469698Z","iopub.execute_input":"2026-03-25T09:36:30.469961Z","iopub.status.idle":"2026-03-25T09:36:31.234315Z","shell.execute_reply.started":"2026-03-25T09:36:30.469929Z","shell.execute_reply":"2026-03-25T09:36:31.233558Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Building the Model","metadata":{}},{"cell_type":"code","source":"model = tf.keras.models.Sequential([\n    layers.Conv2D(128, (3, 3), activation='relu', input_shape=(200, 200, 3)),\n    layers.BatchNormalization(),\n    layers.Activation(\"relu\"),\n    layers.MaxPooling2D(2, 2),\n \n    layers.Conv2D(64, (3, 3), activation='relu'),\n    layers.MaxPooling2D(2, 2),\n    \n    layers.Conv2D(64, (3, 3), activation='relu',),\n    layers.MaxPooling2D(2, 2),\n    \n    layers.Conv2D(64, (3, 3), activation='relu',kernel_regularizer=regularizers.L1L2(l1=1e-5, l2=1e-4),bias_regularizer=regularizers.L2(1e-4),activity_regularizer=regularizers.L2(1e-5)),\n    layers.MaxPooling2D(2, 2),\n    \n    layers.BatchNormalization(),\n\n    layers.Flatten(),\n    \n    layers.Dense(512, activation='relu',),\n    layers.BatchNormalization(),\n    \n    layers.Dense(512, activation='relu'),\n    layers.Dropout(0.1),\n    layers.BatchNormalization(),\n    \n    layers.Dense(512, activation='relu',),\n    layers.Dropout(0.2),\n    layers.BatchNormalization(),\n    \n    layers.Dense(1, activation='sigmoid')\n])\n\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T09:36:31.235307Z","iopub.execute_input":"2026-03-25T09:36:31.235543Z","iopub.status.idle":"2026-03-25T09:36:31.350028Z","shell.execute_reply.started":"2026-03-25T09:36:31.235523Z","shell.execute_reply":"2026-03-25T09:36:31.349498Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Compile the Model\nCompile the model","metadata":{}},{"cell_type":"code","source":"# Configure dataset using data augmentation\ntrainingDataset = trainingDataset.map(\n    lambda image, label: (data_augmentation(image), label),\n    num_parallel_calls=tfd.AUTOTUNE,\n)\n\ntrainingDataset = trainingDataset.map(\n    lambda image, label: (data_rescaling(image), label)\n)\n\nvalidationDataset = validationDataset.map(\n    lambda image, label: (data_rescaling(image), label)\n)\n\ntestDataset = testDataset.map(\n    lambda image, label: (data_rescaling(image), label)\n)\n\n# Prefetch our datasets for better GPU utilization\ntrainingDataset = trainingDataset.prefetch(tfd.AUTOTUNE)\nvalidationDataset = validationDataset.prefetch(tfd.AUTOTUNE)\ntestDataset = testDataset.prefetch(tfd.AUTOTUNE)\n\n# Compile model. Binary cross entropy is selected as we have 2 cases (cat or dog)\nmodel.compile(\n    loss='binary_crossentropy',\n    optimizer='adam',\n    metrics=['accuracy']\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T09:36:31.350825Z","iopub.execute_input":"2026-03-25T09:36:31.351078Z","iopub.status.idle":"2026-03-25T09:36:31.547323Z","shell.execute_reply.started":"2026-03-25T09:36:31.351048Z","shell.execute_reply":"2026-03-25T09:36:31.546718Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train the Model","metadata":{}},{"cell_type":"code","source":"# Reduce learning weight when the model slows down on validation accuracy increase, to combat overfitting on training data\nlearningRateReduction = ReduceLROnPlateau(monitor = 'val_accuracy',\n                                          patience=2,\n                                          factor=0.5,\n                                          min_lr = 0.00001,\n                                          verbose = 1)\n\n# Stop training when validation loss stops improving\nearlyStopping = EarlyStopping(monitor='val_loss',\n                              patience= 3,\n                              restore_best_weights=True,\n                              verbose=1)\n\n# Train the model\nhistory = model.fit(trainingDataset,\n                    epochs=epochs,\n                    callbacks=[earlyStopping,learningRateReduction],\n                    validation_data=validationDataset)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T09:36:31.549426Z","iopub.execute_input":"2026-03-25T09:36:31.549714Z","iopub.status.idle":"2026-03-25T10:20:58.182083Z","shell.execute_reply.started":"2026-03-25T09:36:31.549677Z","shell.execute_reply":"2026-03-25T10:20:58.181302Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Training Evaluation","metadata":{}},{"cell_type":"code","source":"# Plot the change in accuracy and loss over training\nhistoryDataframe = pd.DataFrame(history.history)\nhistoryDataframe.loc[:, ['loss', 'val_loss']].plot()\nhistoryDataframe.loc[:, ['accuracy', 'val_accuracy']].plot()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T10:20:58.183764Z","iopub.execute_input":"2026-03-25T10:20:58.183993Z","iopub.status.idle":"2026-03-25T10:20:58.411061Z","shell.execute_reply.started":"2026-03-25T10:20:58.183970Z","shell.execute_reply":"2026-03-25T10:20:58.410300Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Accuracy Evaluation","metadata":{}},{"cell_type":"code","source":"# Save the model\nmodel.save(\"model.keras\")\n\n# Evaluate accuracy on training and validation data\nloss, accuracy = model.evaluate(trainingDataset,batch_size = batchSize, verbose = 0)\n\nprint('The accuracy of the model for training data is:',accuracy*100)\nprint('The Loss of the model for training data is:',loss)\n\nloss, accuracy = model.evaluate(validationDataset,batch_size = batchSize, verbose = 0)\n\nprint('The accuracy of the model for validation data is:',accuracy*100)\nprint('The Loss of the model for validation data is:',loss)\n\n# Evaluate on test data\n\nloss, accuracy = model.evaluate(testDataset,batch_size = batchSize, verbose = 0)\n\nprint('The accuracy of the model for test data is:',accuracy*100)\nprint('The Loss of the model for test data is:',loss)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T10:20:58.411957Z","iopub.execute_input":"2026-03-25T10:20:58.412257Z","iopub.status.idle":"2026-03-25T10:22:40.611049Z","shell.execute_reply.started":"2026-03-25T10:20:58.412213Z","shell.execute_reply":"2026-03-25T10:22:40.610323Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Predictions","metadata":{}},{"cell_type":"code","source":"# Iterate through the test dataset and collect the labels for each item\n# the test dataset is set not to shuffle\ny_true = []\nfor _, labels in testDataset:\n    y_true.append(labels.numpy())\n\n# Make the axis 0\ny_true = np.concatenate(y_true, axis=0)\n\n# Have the model make predictions on the test data and compare it to the true data\nresult = model.predict(testDataset,batch_size = batchSize,verbose = 0)\n\n# Since we're a binary model, we need to round to 0 or 1 to get our result\ny_prediction = np.round(result).tolist()\n\nlabels =['Cat','Dog']\nprint(classification_report(y_true, y_prediction,target_names=labels))\n\n# Confusion Matrix\nconfusionMatrix = confusion_matrix(y_true,y_prediction) \n\n_, ax = plt.subplots(figsize = (8,4),dpi=200)\nsns.heatmap(confusionMatrix, annot=True, linewidths=0.1, cmap = \"gist_yarg_r\", linecolor=\"black\", fmt='.0f', ax=ax,cbar=False, xticklabels=labels, yticklabels=labels)\n\nplt.xlabel(\"Predicted Label\",fontsize=10)\nplt.ylabel(\"True Label\",fontsize=10)\nplt.title(\"Confusion Matrix\",fontsize=13)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T10:22:40.612105Z","iopub.execute_input":"2026-03-25T10:22:40.612443Z","iopub.status.idle":"2026-03-25T10:22:45.127346Z","shell.execute_reply.started":"2026-03-25T10:22:40.612411Z","shell.execute_reply":"2026-03-25T10:22:45.126498Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### User Input","metadata":{}},{"cell_type":"code","source":"# loadedModel = tf.keras.models.load_model('model.keras')\n\n# Accept user input and get the associated image file\nuserInput = input(\"Input a number to select the test image:\\n\")+\".jpg\"\noutputDir = \"/kaggle/input/datasets/kunalgupta2616/dog-vs-cat-images-data/dogcat/test1/test1/\"\noutputDir = os.path.join(outputDir, userInput)\n\n# load and show the image\nimage = keras.utils.load_img(outputDir, target_size=(200,200))\nimage = data_rescaling(image)\nplt.imshow(image)\n\n# Create batch axis\nimageArray = keras.utils.img_to_array(image)\nimageArray = keras.ops.expand_dims(imageArray, 0)\n\n# Make a prediction on the selected image and output the prediction\npredictions = model.predict(imageArray)\nscore = float(keras.ops.sigmoid(predictions[0][0]))\nprint(score)\nprint(\"Dog\" if score >= 0.5 else \"Cat\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T10:22:45.128288Z","iopub.execute_input":"2026-03-25T10:22:45.128715Z","iopub.status.idle":"2026-03-25T10:22:51.222916Z","shell.execute_reply.started":"2026-03-25T10:22:45.128685Z","shell.execute_reply":"2026-03-25T10:22:51.222174Z"}},"outputs":[],"execution_count":null}]}