{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"},{"sourceId":7280243,"sourceType":"datasetVersion","datasetId":4221137}],"dockerImageVersionId":30626,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Cats vs Dogs Classification using CNN\n\n## Introduction\n\nIn this project, we'll explore a Convolutional Neural Network (CNN) for classifying images of cats and dogs. The dataset includes a mix of both cat and dog images.\n\n![cat_dog](https://s3.amazonaws.com/cdn-origin-etr.akc.org/wp-content/uploads/2018/04/24144817/American-Staffordshire-Terrier-lying-outdoors-next-to-a-kitten-that-is-playing-with-the-dogs-nose.jpg)\n\n## Data Exploration\n\nLet's start by loading and exploring the dataset. We'll visualize a few random images to get a sense of the data.","metadata":{}},{"cell_type":"code","source":"import os\nimport zipfile\nimport random\nimport cv2\nimport numpy as np\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split, RandomizedSearchCV\nfrom sklearn.metrics import confusion_matrix, precision_score, recall_score, f1_score, roc_curve, auc\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.losses import binary_crossentropy\nfrom tensorflow.keras.metrics import Accuracy\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom keras_tuner.tuners import RandomSearch\nfrom keras_tuner.engine.hyperparameters import HyperParameters\nfrom tensorflow.keras.models import Sequential, load_model","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:37:09.156585Z","iopub.execute_input":"2023-12-25T21:37:09.157619Z","iopub.status.idle":"2023-12-25T21:37:09.164974Z","shell.execute_reply.started":"2023-12-25T21:37:09.157580Z","shell.execute_reply":"2023-12-25T21:37:09.164030Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with zipfile.ZipFile(\"/kaggle/input/dogs-vs-cats/train.zip\",\"r\") as z:\n    z.extractall(\".\")\n    \nwith zipfile.ZipFile(\"/kaggle/input/dogs-vs-cats/test1.zip\",\"r\") as z:\n    z.extractall(\".\")\n    \n# Load the images\nmain_dir = \"/kaggle/working/\"\ntrain_dir = \"train\"\npath = os.path.join(main_dir, train_dir)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:37:11.603006Z","iopub.execute_input":"2023-12-25T21:37:11.603741Z","iopub.status.idle":"2023-12-25T21:37:22.495736Z","shell.execute_reply.started":"2023-12-25T21:37:11.603709Z","shell.execute_reply":"2023-12-25T21:37:22.494854Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display 8 random color images from the train folder\nplt.figure(figsize=(15, 10))\nfor i in range(8):\n    random_file = random.choice(os.listdir(path))\n    img_array = cv2.imread(os.path.join(path, random_file))\n    img_array = cv2.cvtColor(img_array, cv2.COLOR_BGR2RGB)\n    plt.subplot(2, 4, i+1)\n    plt.imshow(img_array)\n    plt.axis('off')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:37:32.742933Z","iopub.execute_input":"2023-12-25T21:37:32.743298Z","iopub.status.idle":"2023-12-25T21:37:33.576059Z","shell.execute_reply.started":"2023-12-25T21:37:32.743268Z","shell.execute_reply":"2023-12-25T21:37:33.575122Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Preprocessing\n\nWe'll preprocess the images, normalize pixel values, and split the data into training and validation sets.","metadata":{}},{"cell_type":"code","source":"# Function to load and preprocess images\ndef load_and_preprocess_images(directory, img_size=(80, 80)):\n    images = []\n    labels = []\n\n    for filename in os.listdir(directory):\n        category = filename.split('.')[0]\n        img_array = cv2.imread(os.path.join(directory, filename), cv2.IMREAD_GRAYSCALE)\n        new_img_array = cv2.resize(img_array, dsize=img_size)\n        images.append(new_img_array)\n        labels.append(1 if 'dog' in category else 0)  # Assuming 'dog' is positive class, 'cat' is negative\n\n    return np.array(images), np.array(labels)\n\n# Load and preprocess images\nimages, labels = load_and_preprocess_images(path, img_size=(80, 80))","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:37:57.294576Z","iopub.execute_input":"2023-12-25T21:37:57.295230Z","iopub.status.idle":"2023-12-25T21:38:19.033199Z","shell.execute_reply.started":"2023-12-25T21:37:57.295198Z","shell.execute_reply":"2023-12-25T21:38:19.032349Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Assuming labels are binary (0 for dog, 1 for cat)\ncat_count = np.sum(labels == 0)\ndog_count = np.sum(labels == 1)\n\nlabels_count = [cat_count, dog_count]\nlabels_names = ['Cat', 'Dog']\n\n# Plotting the pie chart\nplt.figure(figsize=(4, 4))\nplt.pie(labels_count, labels=labels_names, autopct='%1.1f%%', startangle=90, colors=['#ff9999','#66b3ff'])\nplt.title('Distribution of Cat and Dog Images')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:39:00.713513Z","iopub.execute_input":"2023-12-25T21:39:00.714263Z","iopub.status.idle":"2023-12-25T21:39:00.827622Z","shell.execute_reply.started":"2023-12-25T21:39:00.714231Z","shell.execute_reply":"2023-12-25T21:39:00.826183Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display 8 random images after labeling\nplt.figure(figsize=(15, 10))\nfor i in range(8):\n    random_index = random.randint(0, len(images)-1)\n    plt.subplot(2, 4, i+1)\n    plt.imshow(images[random_index], cmap='gray')\n    plt.title(f\"Label: {labels[random_index]}\")\n    plt.axis('off')\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:39:02.037902Z","iopub.execute_input":"2023-12-25T21:39:02.038276Z","iopub.status.idle":"2023-12-25T21:39:02.711606Z","shell.execute_reply.started":"2023-12-25T21:39:02.038244Z","shell.execute_reply":"2023-12-25T21:39:02.710671Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Split the data into training and validation sets\ntrain_images, val_images, train_labels, val_labels = train_test_split(images, labels, test_size=0.2, random_state=42)\n\n# Normalize pixel values to be between 0 and 1\ntrain_images, val_images = train_images / 255.0, val_images / 255.0","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:39:04.074448Z","iopub.execute_input":"2023-12-25T21:39:04.075089Z","iopub.status.idle":"2023-12-25T21:39:04.525228Z","shell.execute_reply.started":"2023-12-25T21:39:04.075052Z","shell.execute_reply":"2023-12-25T21:39:04.524087Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create an ImageDataGenerator with data augmentation for training\ntrain_datagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range=20,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    shear_range=0.2,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    fill_mode='nearest'\n)\n\n# Create a generator for both training and validation data with augmentation\ntrain_generator = train_datagen.flow(\n    train_images.reshape((-1, 80, 80, 1)),\n    train_labels,\n    batch_size=32\n)\n\nval_datagen = ImageDataGenerator(rescale=1./255)\nval_generator = val_datagen.flow(\n    val_images.reshape((-1, 80, 80, 1)),\n    val_labels,\n    batch_size=32\n)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:39:05.979921Z","iopub.execute_input":"2023-12-25T21:39:05.980597Z","iopub.status.idle":"2023-12-25T21:39:06.209921Z","shell.execute_reply.started":"2023-12-25T21:39:05.980561Z","shell.execute_reply":"2023-12-25T21:39:06.208803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Training\nThe model is trained using an ImageDataGenerator with data augmentation for the training set.","metadata":{}},{"cell_type":"code","source":"# Define the revised model\nmodel = Sequential([\n    Conv2D(32, (3, 3), activation='relu', input_shape=(80, 80, 1)),\n    BatchNormalization(),\n    MaxPooling2D(2, 2),\n    \n    Conv2D(64, (3, 3), activation='relu'),\n    BatchNormalization(),\n    MaxPooling2D(2, 2),\n    \n    Conv2D(128, (3, 3), activation='relu'),\n    BatchNormalization(),\n    MaxPooling2D(2, 2),\n    \n    Flatten(),\n    Dense(512, activation='relu'),\n    Dropout(0.5),\n    Dense(1, activation='sigmoid')\n])\n\n# Compile the model\nmodel.compile(optimizer=Adam(learning_rate=0.0001), loss=binary_crossentropy, metrics=['accuracy'])\n\n# Implement Early Stopping\nearly_stopping = EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)\n\n# Train the model with the generator for both training and validation\nhistory = model.fit(\n    train_generator,\n    epochs=20,\n    validation_data=val_generator,\n    callbacks=[early_stopping]\n)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:39:13.870574Z","iopub.execute_input":"2023-12-25T21:39:13.871314Z","iopub.status.idle":"2023-12-25T21:42:38.754257Z","shell.execute_reply.started":"2023-12-25T21:39:13.871279Z","shell.execute_reply":"2023-12-25T21:42:38.753287Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Evaluation\nWe evaluate the model's performance using metrics like precision, recall, and F1 score.","metadata":{}},{"cell_type":"code","source":"# Plot training & validation accuracy values\nplt.plot(history.history['accuracy'])\nplt.plot(history.history['val_accuracy'])\nplt.title('Model accuracy')\nplt.ylabel('Accuracy')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Validation'], loc='upper left')\nplt.show()\n\n# Plot training & validation loss values\nplt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('Model loss')\nplt.ylabel('Loss')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Validation'], loc='upper left')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:43:34.073203Z","iopub.execute_input":"2023-12-25T21:43:34.073759Z","iopub.status.idle":"2023-12-25T21:43:34.517965Z","shell.execute_reply.started":"2023-12-25T21:43:34.073713Z","shell.execute_reply":"2023-12-25T21:43:34.517010Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Predict classes for validation set\ny_pred = (model.predict(val_generator) > 0.5).astype(\"int32\")\n\n# Compute confusion matrix\ncm = confusion_matrix(val_labels, y_pred)\n\n# Plot confusion matrix\nsns.heatmap(cm, annot=True, fmt=\"d\", cmap=\"Blues\", xticklabels=['Predicted 0', 'Predicted 1'],\n            yticklabels=['Actual 0', 'Actual 1'])\nplt.title('Confusion Matrix')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:43:40.560567Z","iopub.execute_input":"2023-12-25T21:43:40.561493Z","iopub.status.idle":"2023-12-25T21:43:41.709173Z","shell.execute_reply.started":"2023-12-25T21:43:40.561454Z","shell.execute_reply":"2023-12-25T21:43:41.708227Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"precision = precision_score(val_labels, y_pred)\nrecall = recall_score(val_labels, y_pred)\nf1 = f1_score(val_labels, y_pred)\n\nprint(f'Precision: {precision:.4f}')\nprint(f'Recall: {recall:.4f}')\nprint(f'F1 Score: {f1:.4f}')","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:43:47.374978Z","iopub.execute_input":"2023-12-25T21:43:47.375356Z","iopub.status.idle":"2023-12-25T21:43:47.393114Z","shell.execute_reply.started":"2023-12-25T21:43:47.375323Z","shell.execute_reply":"2023-12-25T21:43:47.392169Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Compute ROC curve\nfpr, tpr, thresholds = roc_curve(val_labels, model.predict(val_generator))\nroc_auc = auc(fpr, tpr)\n\n# Plot ROC curve\nplt.figure()\nplt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (AUC = {roc_auc:.2f})')\nplt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('Receiver Operating Characteristic')\nplt.legend(loc='lower right')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:43:51.015481Z","iopub.execute_input":"2023-12-25T21:43:51.016339Z","iopub.status.idle":"2023-12-25T21:43:51.945276Z","shell.execute_reply.started":"2023-12-25T21:43:51.016300Z","shell.execute_reply":"2023-12-25T21:43:51.944299Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Hyperparameter Tuning\nWe perform hyperparameter tuning using Keras Tuner to find the optimal set of hyperparameters for our model.","metadata":{}},{"cell_type":"code","source":"# Function to build the model with hyperparameters\ndef create_model(hp):\n    model = Sequential([\n        Conv2D(hp.Int('conv1_units', min_value=32, max_value=128, step=32), (3, 3), activation='relu', input_shape=(80, 80, 1)),\n        BatchNormalization(),\n        MaxPooling2D(2, 2),\n\n        Conv2D(hp.Int('conv2_units', min_value=32, max_value=128, step=32), (3, 3), activation='relu'),\n        BatchNormalization(),\n        MaxPooling2D(2, 2),\n\n        Conv2D(hp.Int('conv3_units', min_value=32, max_value=128, step=32), (3, 3), activation='relu'),\n        BatchNormalization(),\n        MaxPooling2D(2, 2),\n\n        Flatten(),\n        Dense(hp.Int('dense_units', min_value=128, max_value=512, step=64), activation='relu'),\n        Dropout(hp.Float('dropout', min_value=0.3, max_value=0.7, step=0.1)),\n        Dense(1, activation='sigmoid')\n    ])\n\n    model.compile(\n        optimizer=Adam(learning_rate=hp.Float('learning_rate', min_value=1e-5, max_value=1e-2, sampling='log')),\n        loss=binary_crossentropy,\n        metrics=['accuracy']\n    )\n\n    return model\n\n# Set up hyperparameter tuner\nmodel_hp = RandomSearch(\n    create_model,\n    objective='val_accuracy',\n    max_trials=20,\n    directory='keras_tuner_dir',\n    project_name='cats_vs_dogs'\n)\n\n# Perform the hyperparameter search with Early Stopping\nmodel_hp.search(\n    train_generator,\n    epochs=20,\n    validation_data=val_generator,\n    callbacks=[EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)]\n)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:43:55.567922Z","iopub.execute_input":"2023-12-25T21:43:55.568963Z","iopub.status.idle":"2023-12-25T21:43:55.600473Z","shell.execute_reply.started":"2023-12-25T21:43:55.568900Z","shell.execute_reply":"2023-12-25T21:43:55.599726Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Best Model Training\nFinally, we train the model using the best hyperparameters on the entire training set.","metadata":{}},{"cell_type":"code","source":"# Function to load and preprocess test images\ndef load_and_preprocess_test_images(directory, img_size=(80, 80)):\n    images = []\n\n    for filename in os.listdir(directory):\n        img_array = cv2.imread(os.path.join(directory, filename), cv2.IMREAD_GRAYSCALE)\n        new_img_array = cv2.resize(img_array, dsize=img_size)\n        images.append(new_img_array)\n\n    return np.array(images)\n\n# Directory containing test images\ntest_dir = \"test1\"\ntest_directory = os.path.join(main_dir, test_dir)\n\n# Load and preprocess test images\ntest_images = load_and_preprocess_test_images(test_directory, img_size=(80, 80))\n\n# Reshape test images to match the model's input shape\ntest_images = test_images.reshape((-1, 80, 80, 1))\n\n# Get the best trial from the hyperparameter search\nbest_trial = model_hp.oracle.get_best_trials(num_trials=1)[0]\n\n# Build the model with the best hyperparameters\nbest_model = create_model(best_trial.hyperparameters)\n\n# Compile the model\nbest_model.compile(\n    optimizer=Adam(learning_rate=best_trial.hyperparameters.get('learning_rate')),\n    loss=binary_crossentropy,\n    metrics=['accuracy']\n)\n\n# Set up early stopping and model checkpoint callbacks\nearly_stopping = EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)\nmodel_checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True)\n\n# Train the best model on the entire training data\nbest_model.fit(\n    train_generator,\n    epochs=20,\n    validation_data=val_generator,\n    callbacks=[early_stopping, model_checkpoint]\n)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:48:00.943642Z","iopub.execute_input":"2023-12-25T21:48:00.944049Z","iopub.status.idle":"2023-12-25T21:52:19.207978Z","shell.execute_reply.started":"2023-12-25T21:48:00.944017Z","shell.execute_reply":"2023-12-25T21:52:19.206947Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load the best model from the saved file\nbest_model = load_model('best_model.h5')\n\n# Use the best model to make predictions on the test data\ntest_predictions = best_model.predict(test_images)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:54:08.552952Z","iopub.execute_input":"2023-12-25T21:54:08.553923Z","iopub.status.idle":"2023-12-25T21:54:12.340054Z","shell.execute_reply.started":"2023-12-25T21:54:08.553877Z","shell.execute_reply":"2023-12-25T21:54:12.339032Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display some predicted images\ndef display_predicted_images(images, predictions, num_images=4):\n    plt.figure(figsize=(15, 5))\n    for i in range(num_images):\n        plt.subplot(1, num_images, i + 1)\n        plt.imshow(images[i].reshape((80, 80)), cmap='gray')\n        plt.title(f'Predicted: {predictions[i][0]:.2f}')\n        plt.axis('off')\n    plt.show()\n\n# Display predicted images with actual labels\ndisplay_predicted_images(test_images, test_predictions, num_images=4)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T21:55:14.509492Z","iopub.execute_input":"2023-12-25T21:55:14.510227Z","iopub.status.idle":"2023-12-25T21:55:14.934836Z","shell.execute_reply.started":"2023-12-25T21:55:14.510194Z","shell.execute_reply":"2023-12-25T21:55:14.933731Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Conclusion\nIn conclusion, our model achieved satisfactory performance in classifying cat and dog images. We've demonstrated the process of data preprocessing, model building, training, and evaluation.\n\n# Made by: Abdelrahman Eldaba","metadata":{}}]}