{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Dogs vs Cats Classification Project\n**Akbank Deep Learning Bootcamp – New Generation Project**\n\nIn this project, we build a deep learning model to classify images of dogs and cats. We use a Convolutional Neural Network (CNN) for image recognition. The main goal is to get practical experience in image classification, data analysis, model building, evaluation, and interpretation.\n\n## Project Steps\n\n1. **Setup Environment**  \n   - Use Kaggle notebooks to run the project and save results.  \n   - Create a `README.md` explaining the project, dataset, methods, and results.\n\n2. **Data Preprocessing**  \n   - Label, clean, and split images into training, validation, and test sets.  \n   - Use data augmentation (rotation, flip, zoom, color changes) to improve model performance.\n\n3. **Model Training**  \n   - Build a CNN with convolutional, pooling, dense layers, and dropout.  \n   - The model learns patterns and classifies images as dogs or cats.\n\n4. **Model Evaluation**  \n   - Check accuracy and loss graphs.  \n   - Create confusion matrix and classification report.  \n   - Use Grad-CAM to visualize which parts of the images the model focuses on.\n\n5. **Hyperparameter Optimization**  \n   - Experiment with number of layers, filters, dropout rate, learning rate, batch size, and optimizer.\n\n## Dataset\n\n- **Dogs vs Cats** from Kaggle  \n- ~25,000 training images, 12,500 test images  \n- Labels: `0 = Dog`, `1 = Cat`  \n\nThis project helps practice deep learning, image processing, and model evaluation with real-world data.\n","metadata":{}},{"cell_type":"code","source":"# Basic Data Processing\nimport pandas as pd\nimport numpy as np\nimport os\nimport random\nfrom pathlib import Path\nimport zipfile\n\n# Image Processing\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array\nimport shutil\nimport cv2\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# Deep Learning Framework\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers, models, optimizers, callbacks\nfrom tensorflow.keras.models import Model, load_model\nfrom tensorflow.keras.applications import VGG16, ResNet50\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\nimport tensorflow.keras.callbacks as keras_callback\n\n# Model Evaluation\nfrom sklearn.metrics import classification_report, confusion_matrix\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras.preprocessing import image\n# Visualization\nimport plotly.express as px\nimport plotly.graph_objects as go\n\n# Progress Bar\nfrom tqdm import tqdm\n\n# Warnings\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Extras for Grad-CAM\nimport tensorflow.keras.backend as K\n\n# Test imports\nprint(\"✅ All libraries imported successfully!\")\nprint(f\"TensorFlow version: {tf.__version__}\")\nprint(f\"GPU Available: {tf.config.list_physical_devices('GPU')}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-25T17:54:22.254036Z","iopub.execute_input":"2025-09-25T17:54:22.254353Z","iopub.status.idle":"2025-09-25T17:54:22.261812Z","shell.execute_reply.started":"2025-09-25T17:54:22.254330Z","shell.execute_reply":"2025-09-25T17:54:22.261139Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2.Dataset Setup and Extraction\n\nIn this section, we define the file paths for the Dogs vs Cats dataset and prepare the working directories. The code checks if the training and test zip files have already been extracted, and if not, it extracts them to the specified folders. After extraction, it counts the number of files in the training and test directories to make sure all images are available. This step ensures that the dataset is organized and ready for preprocessing and model training.\n","metadata":{}},{"cell_type":"code","source":"# Define paths\nbase_path = '/kaggle/input/dogs-vs-cats'\ntrain_zip = os.path.join(base_path, 'train.zip')\ntest_zip = os.path.join(base_path, 'test1.zip')\n\n# Extract directories\nextract_path = '/kaggle/working/dogs-vs-cats-data'\nTRAIN_DIR = os.path.join(extract_path, 'train')\nTEST_DIR = os.path.join(extract_path, 'test1')\n\nprint(\"\\nChecking dataset directories...\")\n\n# Extract train.zip if not already extracted\nif not os.path.exists(TRAIN_DIR):\n    print(\"Extracting train.zip...\")\n    os.makedirs(extract_path, exist_ok=True)\n    with zipfile.ZipFile(train_zip, 'r') as zip_ref:\n        zip_ref.extractall(extract_path)\n    print(\"Train extraction completed!\")\nelse:\n    print(\"Train directory already exists\")\n\n# Extract test1.zip if not already extracted  \nif not os.path.exists(TEST_DIR):\n    print(\"Extracting test1.zip...\")\n    os.makedirs(extract_path, exist_ok=True)\n    with zipfile.ZipFile(test_zip, 'r') as zip_ref:\n        zip_ref.extractall(extract_path)\n    print(\"Test extraction completed!\")\nelse:\n    print(\"Test directory already exists\")\n\n# Check if directories exist and count files\nif os.path.exists(TRAIN_DIR):\n    train_files = os.listdir(TRAIN_DIR)\n    print(\"Training directory found:\", len(train_files), \"files\")\nelse:\n    print(\"Training directory not found!\")\n    train_files = []\n\nif os.path.exists(TEST_DIR):\n    test_files = os.listdir(TEST_DIR)\n    print(\"Test directory found:\", len(test_files), \"files\")\nelse:\n    print(\"Test directory not found!\")\n    test_files = []\n\nprint(\"\\nFinal paths:\")\nprint(\"TRAIN_DIR:\", TRAIN_DIR)\nprint(\"TEST_DIR:\", TEST_DIR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-25T17:54:22.582585Z","iopub.execute_input":"2025-09-25T17:54:22.583259Z","iopub.status.idle":"2025-09-25T17:54:22.609853Z","shell.execute_reply.started":"2025-09-25T17:54:22.583234Z","shell.execute_reply":"2025-09-25T17:54:22.609136Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3.Dataset Exploration and Visualization\n\nThis section explores the Dogs vs Cats dataset to understand its content and structure. First, it counts how many dog and cat images are in the training set and shows a few sample images from each class. Then, it analyzes image sizes, displaying minimum, maximum, and average width and height, along with histograms to see the distribution. Finally, it visualizes the class distribution with a pie chart to show the proportion of dogs and cats. This exploration helps us understand the dataset before starting preprocessing and training the model.\n","metadata":{}},{"cell_type":"code","source":"def explore_dataset():\n    print(\"\\nExploring dataset...\")\n    \n    if not train_files:\n        print(\"No training files found!\")\n        return [], [], []\n    \n    # Count dogs and cats\n    dog_files = []\n    cat_files = []\n    \n    for filename in train_files:\n        if 'dog' in filename.lower():\n            dog_files.append(filename)\n        elif 'cat' in filename.lower():\n            cat_files.append(filename)\n    \n    print(\"Dog images:\", len(dog_files))\n    print(\"Cat images:\", len(cat_files))\n    print(\"Total training images:\", len(train_files))\n    \n    if dog_files:\n        print(\"Sample dog files:\", dog_files[:3])\n    if cat_files:\n        print(\"Sample cat files:\", cat_files[:3])\n    \n    return train_files, dog_files, cat_files\n\n# Run exploration\nif train_files:\n    train_files, dog_files, cat_files = explore_dataset()\nelse:\n    dog_files, cat_files = [], []\n\n# Display sample images\ndef show_sample_images():\n    print(\"\\nShowing sample images...\")\n    \n    if not train_files:\n        print(\"No images to display!\")\n        return\n    \n    fig, axes = plt.subplots(2, 4, figsize=(15, 8))\n    fig.suptitle('Dogs vs Cats - Sample Images', fontsize=16)\n    \n    if dog_files and cat_files:\n        # Show 4 dog images\n        for i in range(min(4, len(dog_files))):\n            img_path = os.path.join(TRAIN_DIR, dog_files[i])\n            img = load_img(img_path, target_size=(150, 150))\n            axes[0, i].imshow(img)\n            axes[0, i].set_title('Dog ' + str(i+1))\n            axes[0, i].axis('off')\n        \n        # Show 4 cat images\n        for i in range(min(4, len(cat_files))):\n            img_path = os.path.join(TRAIN_DIR, cat_files[i])\n            img = load_img(img_path, target_size=(150, 150))\n            axes[1, i].imshow(img)\n            axes[1, i].set_title('Cat ' + str(i+1))\n            axes[1, i].axis('off')\n    else:\n        # Show first 8 images\n        for i in range(min(8, len(train_files))):\n            row = i // 4\n            col = i % 4\n            img_path = os.path.join(TRAIN_DIR, train_files[i])\n            try:\n                img = load_img(img_path, target_size=(150, 150))\n                axes[row, col].imshow(img)\n                axes[row, col].set_title('Image ' + str(i+1))\n                axes[row, col].axis('off')\n            except:\n                axes[row, col].text(0.5, 0.5, 'Error', ha='center', va='center')\n                axes[row, col].axis('off')\n    \n    plt.tight_layout()\n    plt.show()\n\n# Show images\nif train_files:\n    show_sample_images()\n\n# Analyze image sizes\ndef analyze_images(sample_size=100):\n    print(\"\\nAnalyzing image sizes...\")\n    \n    if not train_files:\n        print(\"No images to analyze!\")\n        return\n    \n    widths = []\n    heights = []\n    \n    # Take random sample\n    sample_files = random.sample(train_files, min(sample_size, len(train_files)))\n    \n    for filename in sample_files:\n        img_path = os.path.join(TRAIN_DIR, filename)\n        try:\n            with Image.open(img_path) as img:\n                width, height = img.size\n                widths.append(width)\n                heights.append(height)\n        except:\n            continue\n    \n    if widths:\n        print(\"Width - Min:\", min(widths), \"Max:\", max(widths), \"Average:\", int(np.mean(widths)))\n        print(\"Height - Min:\", min(heights), \"Max:\", max(heights), \"Average:\", int(np.mean(heights)))\n        \n        # Plot histograms\n        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))\n        \n        ax1.hist(widths, bins=20, color='blue', alpha=0.7)\n        ax1.set_title('Image Width Distribution')\n        ax1.set_xlabel('Width (pixels)')\n        ax1.set_ylabel('Count')\n        \n        ax2.hist(heights, bins=20, color='red', alpha=0.7)\n        ax2.set_title('Image Height Distribution') \n        ax2.set_xlabel('Height (pixels)')\n        ax2.set_ylabel('Count')\n        \n        plt.tight_layout()\n        plt.show()\n\n# Analyze images\nif train_files:\n    analyze_images()\n\n# Show class distribution\ndef show_class_distribution():\n    print(\"\\nClass distribution:\")\n    \n    if not dog_files and not cat_files:\n        print(\"Cannot show distribution - no labeled data!\")\n        return\n    \n    # Create pie chart\n    labels = ['Dogs', 'Cats']\n    sizes = [len(dog_files), len(cat_files)]\n    colors = ['lightblue', 'lightcoral']\n    \n    plt.figure(figsize=(8, 6))\n    plt.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)\n    plt.title('Dogs vs Cats Distribution')\n    plt.axis('equal')\n    plt.show()\n    \n    print(\"Dogs:\", len(dog_files), \"images\")\n    print(\"Cats:\", len(cat_files), \"images\")\n    total = len(dog_files) + len(cat_files)\n    print(\"Dog percentage:\", round(len(dog_files)/total*100, 1), \"%\")\n    print(\"Cat percentage:\", round(len(cat_files)/total*100, 1), \"%\")\n\n# Show distribution\nif dog_files or cat_files:\n    show_class_distribution()\n\nprint(\"\\nDataset setup completed!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-25T17:54:22.925637Z","iopub.execute_input":"2025-09-25T17:54:22.925894Z","iopub.status.idle":"2025-09-25T17:54:24.404939Z","shell.execute_reply.started":"2025-09-25T17:54:22.925875Z","shell.execute_reply":"2025-09-25T17:54:24.404207Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 4.Data Preprocessing\n\nIn this step, we prepare the dataset for training the CNN model. First, we assign labels to the images: dogs = 0 and cats = 1. Then, we split the data into training (60%), validation (20%), and test sets (20%). Next, we organize the files into separate folders for each set and class. After that, we create data generators using TensorFlow's `ImageDataGenerator`. The training generator includes data augmentation like rotation, shifting, zoom, flipping, and brightness changes, while validation and test generators only normalize the images. Finally, we verify the preprocessing by displaying a few preprocessed images and check the summary of image sizes, batch size, and number of samples. This step ensures that the model will receive properly labeled and augmented data for effective training.\n","metadata":{}},{"cell_type":"code","source":"print(\"Starting data preprocessing...\")\n\n# STEP 1: CREATE LABELS AND SPLIT DATA\nprint(\"\\nStep 1: Creating labels and splitting data...\")\n\n# Create lists for image paths and labels\nimage_paths = []\nlabels = []\n\nfor filename in train_files:\n    full_path = os.path.join(TRAIN_DIR, filename)\n    image_paths.append(full_path)\n    \n    # Label: dogs=0, cats=1\n    if 'dog' in filename.lower():\n        labels.append(0)\n    elif 'cat' in filename.lower():\n        labels.append(1)\n\nprint(\"Total labeled images:\", len(labels))\nprint(\"Dogs:\", labels.count(0), \"Cats:\", labels.count(1))\n\n# Split data: 60% train, 20% validation, 20% test\ntrain_paths, test_paths, train_labels, test_labels = train_test_split(\n    image_paths, labels, test_size=0.2, random_state=42, stratify=labels\n)\n\ntrain_paths, val_paths, train_labels, val_labels = train_test_split(\n    train_paths, train_labels, test_size=0.25, random_state=42, stratify=train_labels\n)\n\nprint(f\"Train: {len(train_paths)}, Validation: {len(val_paths)}, Test: {len(test_paths)}\")\n\n# STEP 2: ORGANIZE FILES INTO FOLDERS\nprint(\"\\nStep 2: Organizing files...\")\n\n# Create directory structure\norganized_dir = '/kaggle/working/organized_data'\ndirs = [\n    os.path.join(organized_dir, 'train', 'dogs'),\n    os.path.join(organized_dir, 'train', 'cats'),\n    os.path.join(organized_dir, 'validation', 'dogs'),\n    os.path.join(organized_dir, 'validation', 'cats'),\n    os.path.join(organized_dir, 'test', 'dogs'),\n    os.path.join(organized_dir, 'test', 'cats')\n]\n\nfor directory in dirs:\n    os.makedirs(directory, exist_ok=True)\n\n# Copy files to organized structure\ndef copy_files(paths, labels, base_dir):\n    for path, label in zip(paths, labels):\n        filename = os.path.basename(path)\n        if label == 0:  # Dog\n            dest = os.path.join(base_dir, 'dogs', filename)\n        else:  # Cat\n            dest = os.path.join(base_dir, 'cats', filename)\n        \n        if not os.path.exists(dest):\n            shutil.copy2(path, dest)\n\ncopy_files(train_paths, train_labels, os.path.join(organized_dir, 'train'))\ncopy_files(val_paths, val_labels, os.path.join(organized_dir, 'validation'))\ncopy_files(test_paths, test_labels, os.path.join(organized_dir, 'test'))\n\nprint(\"Files organized successfully!\")\n\n# STEP 3: CREATE DATA GENERATORS\nprint(\"\\nStep 3: Creating data generators...\")\n\n# Training generator with data augmentation\ntrain_datagen = ImageDataGenerator(\n    rescale=1./255,              # Normalize pixels to 0-1\n    rotation_range=20,           # Rotate images\n    width_shift_range=0.1,       # Shift horizontally\n    height_shift_range=0.1,      # Shift vertically\n    zoom_range=0.1,              # Zoom in/out\n    horizontal_flip=True,        # Flip horizontally\n    brightness_range=[0.8, 1.2], # Change brightness\n    fill_mode='nearest'\n)\n\n# Validation and test generators (no augmentation)\nval_test_datagen = ImageDataGenerator(rescale=1./255)\n\n# Create data flows\nIMG_SIZE = 128  # ← MODEL INPUT İLE UYUMLU\nBATCH_SIZE = 32\n\ntrain_generator = train_datagen.flow_from_directory(\n    os.path.join(organized_dir, 'train'),\n    target_size=(IMG_SIZE, IMG_SIZE),  # 128x128\n    batch_size=BATCH_SIZE,\n    class_mode='binary',\n    classes=['dogs', 'cats'],  # dogs=0, cats=1\n    shuffle=True,\n    seed=42\n)\n\nvalidation_generator = val_test_datagen.flow_from_directory(\n    os.path.join(organized_dir, 'validation'),\n    target_size=(IMG_SIZE, IMG_SIZE),  # 128x128\n    batch_size=BATCH_SIZE,\n    class_mode='binary',\n    classes=['dogs', 'cats'],\n    shuffle=False\n)\n\ntest_generator = val_test_datagen.flow_from_directory(\n    os.path.join(organized_dir, 'test'),\n    target_size=(IMG_SIZE, IMG_SIZE),  # 128x128\n    batch_size=BATCH_SIZE,\n    class_mode='binary',\n    classes=['dogs', 'cats'],\n    shuffle=False\n)\n\nprint(\"Data generators created!\")\nprint(\"Training samples:\", train_generator.samples)\nprint(\"Validation samples:\", validation_generator.samples)\nprint(\"Test samples:\", test_generator.samples)\n\n# STEP 4: VERIFY PREPROCESSING\nprint(\"\\nStep 4: Verifying preprocessing...\")\n\ndef show_preprocessed_samples():\n    \"\"\"Show sample preprocessed images with correct labels\"\"\"\n    images, labels = next(train_generator)\n    \n    fig, axes = plt.subplots(2, 4, figsize=(15, 8))\n    fig.suptitle('Preprocessed Images with Correct Labels', fontsize=16)\n    \n    for i in range(8):\n        row = i // 4\n        col = i % 4\n        \n        axes[row, col].imshow(images[i])\n        \n        if labels[i] == 0:\n            class_name = \"Dog\"\n            color = 'blue'\n        else:\n            class_name = \"Cat\"\n            color = 'red'\n        \n        axes[row, col].set_title(f'{class_name} (Label: {int(labels[i])})', \n                                color=color, fontweight='bold')\n        axes[row, col].axis('off')\n    \n    plt.tight_layout()\n    plt.show()\n\nshow_preprocessed_samples()\n\n# STEP 5: SUMMARY\nprint(\"\\nPreprocessing Summary:\")\nprint(\"=\" * 40)\nprint(f\"Image size: {IMG_SIZE}x{IMG_SIZE}\")\nprint(f\"Batch size: {BATCH_SIZE}\")\nprint(f\"Classes: Dogs=0, Cats=1\")\nprint(f\"Training images: {train_generator.samples}\")\nprint(f\"Validation images: {validation_generator.samples}\")\nprint(f\"Test images: {test_generator.samples}\")\nprint(f\"Steps per epoch: {train_generator.samples // BATCH_SIZE}\")\nprint(\"\\nData augmentation applied to training set:\")\nprint(\"- Rotation, shifting, zoom, flip, brightness\")\nprint(\"\\nData preprocessing completed!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-25T17:54:24.406328Z","iopub.execute_input":"2025-09-25T17:54:24.406820Z","iopub.status.idle":"2025-09-25T17:54:26.081242Z","shell.execute_reply.started":"2025-09-25T17:54:24.406802Z","shell.execute_reply":"2025-09-25T17:54:26.080453Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 5.CNN Model Building\n\nIn this step, we build a Convolutional Neural Network (CNN) for the Dogs vs Cats classification. A CNN is a special type of neural network for images that can detect patterns like edges, shapes, and textures. First, we create a basic CNN with three convolutional blocks, each followed by max pooling, a flatten layer, a dense layer, and a dropout layer to prevent overfitting. The output layer uses sigmoid activation for binary classification (dog=0, cat=1). Then, we create an improved CNN with four convolutional blocks and additional dense layers for better feature learning. Both models are compiled using the Adam optimizer, binary crossentropy loss, and accuracy metric. We also set up callbacks like early stopping and learning rate reduction to improve training. Finally, we review the model summaries and check the total trainable parameters, preparing the model for training with the preprocessed data.\n","metadata":{}},{"cell_type":"code","source":"print(\"Starting CNN model building...\")\n\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n\n# STEP 1: CREATE SIMPLIFIED CNN MODEL\nprint(\"\\nStep 1: Creating simplified CNN architecture...\")\n\ndef create_simplified_cnn():\n    \"\"\"\n    Create a simplified CNN model\n    \"\"\"\n    model = models.Sequential()\n    \n    # INPUT\n    model.add(layers.Input(shape=(128, 128, 3)))\n    \n    # BLOCK 1\n    model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))\n    model.add(layers.MaxPooling2D((2, 2)))\n    model.add(layers.Dropout(0.25))\n    \n    # BLOCK 2\n    model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))\n    model.add(layers.MaxPooling2D((2, 2)))\n    model.add(layers.Dropout(0.25))\n    \n    # BLOCK 3\n    model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))\n    model.add(layers.MaxPooling2D((2, 2)))\n    model.add(layers.Dropout(0.25))\n    \n    # CLASSIFIER HEAD\n    model.add(layers.Flatten())\n    model.add(layers.Dense(256, activation='relu'))\n    model.add(layers.Dropout(0.5))\n    model.add(layers.Dense(1, activation='sigmoid'))\n    \n    return model\n\n# Create simplified model\nsimplified_model = create_simplified_cnn()\n\nprint(\"Simplified CNN Model Summary:\")\nsimplified_model.summary()\n\n# Compile simplified model\nsimplified_model.compile(\n    optimizer=Adam(learning_rate=0.0005),  # biraz daha hızlı öğrenme\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)\n\nprint(\"Simplified model compiled!\")\n\n# STEP 2: SETUP TRAINING CALLBACKS\nprint(\"\\nStep 2: Setting up training callbacks...\")\n\nearly_stopping = EarlyStopping(\n    monitor='val_accuracy',\n    patience=5,\n    restore_best_weights=True,\n    verbose=1\n)\n\nreduce_lr = ReduceLROnPlateau(\n    monitor='val_loss',\n    factor=0.2,\n    patience=3,\n    min_lr=0.00001,\n    verbose=1\n)\n\ncallbacks = [early_stopping, reduce_lr]\n\nprint(\"Callbacks configured.\")\n\n# STEP 3: PREPARE FOR TRAINING\nprint(\"\\nStep 3: Preparing for training...\")\n\n# Yeni batch size = 64\ntrain_generator.batch_size = 64\nvalidation_generator.batch_size = 64\n\n# Epoch sayısı 15\nEPOCHS = 15\n\ntrain_steps = train_generator.samples // train_generator.batch_size\nval_steps = validation_generator.samples // validation_generator.batch_size\n\nprint(f\"Training steps per epoch: {train_steps}\")\nprint(f\"Validation steps per epoch: {val_steps}\")\nprint(f\"Epochs: {EPOCHS}\")\n\n# STEP 4: READY TO TRAIN\nprint(\"\\nModel Training Setup Summary:\")\nprint(\"=\" * 50)\nprint(\"✓ Simplified CNN model created (3 conv blocks)\")\nprint(\"✓ Input size: 128x128x3\")\nprint(\"✓ Batch size: 64\")\nprint(\"✓ Epochs: 15\")\nprint(\"✓ Optimizer: Adam (lr=0.0005)\")\nprint(\"✓ Binary crossentropy loss\")\nprint(\"✓ EarlyStopping & ReduceLROnPlateau callbacks\")\nprint(\"✓ Ready for training!\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-25T17:54:26.082011Z","iopub.execute_input":"2025-09-25T17:54:26.082286Z","iopub.status.idle":"2025-09-25T17:54:26.209435Z","shell.execute_reply.started":"2025-09-25T17:54:26.082266Z","shell.execute_reply":"2025-09-25T17:54:26.208800Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 6.Model Training\n\nIn this step, we train the chosen CNN model using the preprocessed data. We set the number of epochs to 20 and use early stopping to prevent overfitting if the validation loss stops improving. During training, the model learns to recognize dogs and cats from the images. After training, we plot graphs for accuracy and loss to see how the model performed on both training and validation sets. Finally, we save the trained model and its weights to files so we can use it later without retraining. We also save the training history for reference. This completes the training and preparation for model evaluation.\n","metadata":{}},{"cell_type":"code","source":"print(\"Starting model training...\")\n\n# Step 1: Define callbacks (simple version)\ncallbacks = [\n    EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)\n]\n\n# Step 2: Train the model\nEPOCHS = 15\nprint(f\"Training for {EPOCHS} epochs...\")\nprint(f\"Training images: {train_generator.samples}\")\nprint(f\"Validation images: {validation_generator.samples}\")\n\nhistory = simplified_model.fit(\n    train_generator,\n    steps_per_epoch=train_steps,\n    epochs=EPOCHS,\n    validation_data=validation_generator,\n    validation_steps=val_steps,\n    callbacks=callbacks,\n    verbose=1\n)\n\nprint(\"Training completed!\")\n\n# Step 3: Plot Accuracy & Loss graphs\ndef plot_training_history(history):\n    acc = history.history['accuracy']\n    val_acc = history.history['val_accuracy']\n    loss = history.history['loss']\n    val_loss = history.history['val_loss']\n    epochs_range = range(len(acc))\n    \n    plt.figure(figsize=(12, 4))\n    \n    # Accuracy plot\n    plt.subplot(1, 2, 1)\n    plt.plot(epochs_range, acc, label='Training Accuracy')\n    plt.plot(epochs_range, val_acc, label='Validation Accuracy')\n    plt.legend()\n    plt.title('Accuracy (Training vs Validation)')\n    \n    # Loss plot\n    plt.subplot(1, 2, 2)\n    plt.plot(epochs_range, loss, label='Training Loss')\n    plt.plot(epochs_range, val_loss, label='Validation Loss')\n    plt.legend()\n    plt.title('Loss (Training vs Validation)')\n    \n    plt.show()\n    \n    # Print final metrics\n    print(f\"Final Training Accuracy: {acc[-1]:.4f}\")\n    print(f\"Final Validation Accuracy: {val_acc[-1]:.4f}\")\n    print(f\"Final Training Loss: {loss[-1]:.4f}\")\n    print(f\"Final Validation Loss: {val_loss[-1]:.4f}\")\n\nplot_training_history(history)\n\n# Step 4: Save the trained model\nprint(\"\\nStep 4: Saving trained model...\")\n\ntry:\n    # Save complete model\n    model_path = '/kaggle/working/dogs_cats_trained_model.h5'\n    simplified_model.save(model_path)\n    print(f\"✓ Model saved successfully: {model_path}\")\n    \n    # Save weights separately (backup)\n    weights_path = '/kaggle/working/model_weights.h5'\n    simplified_model.save_weights(weights_path)\n    print(f\"✓ Weights saved successfully: {weights_path}\")\n    \n    # Check file sizes\n    import os\n    model_size = os.path.getsize(model_path) / (1024*1024)  # MB\n    weights_size = os.path.getsize(weights_path) / (1024*1024)  # MB\n    \n    print(f\"✓ Model file size: {model_size:.1f} MB\")\n    print(f\"✓ Weights file size: {weights_size:.1f} MB\")\n    \n    # Test loading to make sure it works\n    print(\"\\nTesting model loading...\")\n    from tensorflow.keras.models import load_model\n    test_model = load_model(model_path)\n    print(\"✓ Model loading test successful!\")\n    \n    # Save training history too\n    import pickle\n    history_path = '/kaggle/working/training_history.pkl'\n    with open(history_path, 'wb') as f:\n        pickle.dump(history.history, f)\n    print(f\"✓ Training history saved: {history_path}\")\n    \nexcept Exception as e:\n    print(f\" Error saving model: {e}\")\n    print(\"Training completed but model not saved!\")\n\nprint(\"\\n\" + \"=\"*50)\nprint(\"TRAINING AND SAVING COMPLETED!\")\nprint(\"=\"*50)\nprint(\"✓ Model trained successfully\")\nprint(\"✓ Training graphs displayed\")\nprint(\"✓ Model saved to file\")\nprint(\"✓ Ready for evaluation!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-25T17:54:26.210742Z","iopub.execute_input":"2025-09-25T17:54:26.210946Z","iopub.status.idle":"2025-09-25T18:00:25.638690Z","shell.execute_reply.started":"2025-09-25T17:54:26.210930Z","shell.execute_reply":"2025-09-25T18:00:25.637827Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 7.Model Evaluation\n\nThis section evaluates the trained CNN model for dogs vs cats using the saved model and test data. Key steps include:\n\n1. **Load or Recreate Model**  \n   - The trained model is loaded from the saved `.h5` file.  \n   - If loading fails, the improved CNN architecture is recreated and compiled (weights are untrained).\n\n2. **Data Generators**  \n   - Ensures the test data generator exists and is ready for evaluation.  \n   - If needed, the generator is recreated to provide batches of preprocessed test images.\n\n3. **Evaluate on Test Set**  \n   - Computes test loss and accuracy using `model.evaluate`.  \n   - This provides an overall measure of model performance on unseen data.\n\n4. **Predictions and Labels**  \n   - Generates predictions for all test images.  \n   - Converts predicted probabilities to binary labels (`0=dog`, `1=cat`).  \n   - True labels are obtained from the test generator.\n\n5. **Confusion Matrix**  \n   - Plots a heatmap to visualize correct and incorrect predictions.  \n   - Calculates standard metrics: accuracy, precision, recall, F1-score.  \n   - Helps understand which class the model predicts well or struggles with.\n\n6. **Classification Report**  \n   - Provides detailed metrics per class: precision, recall, F1-score, support.  \n   - Useful for a thorough analysis of model performance.\n\n7. **Grad-CAM Visualization**  \n   - Simplified Grad-CAM implementation highlights which image regions influence predictions.  \n   - Visualizes the model’s attention for both dogs and cats in sample test images.\n\n**Summary of Evaluation Results:**\n- The saved model is successfully loaded and tested.  \n- Accuracy and loss on the test set are reported.  \n- Confusion matrix and classification report provide detailed performance metrics.  \n- Grad-CAM heatmaps show the model’s focus areas for predictions.  \n\nThis workflow ensures the trained CNN is properly evaluated and interpretable, confirming its ability to distinguish dogs from cats.\n","metadata":{}},{"cell_type":"code","source":"# Loading saved model and running evaluation\n\nprint(\"Starting model evaluation with saved model...\")\n\n# STEP 1: LOAD OR RECREATE MODEL\nprint(\"\\nStep 1: Loading or recreating model...\")\n\n\n\n# First, try to load saved model\nmodel_path = \"/kaggle/working/dogs_cats_trained_model.h5\"\nloaded_model = None\n\nif os.path.exists(model_path):\n    try:\n        loaded_model = load_model(model_path)\n        print(\"✓ Model loaded from file successfully!\")\n    except Exception as e:\n        print(f\"Error loading model file: {e}\")\n        loaded_model = None\nelse:\n    print(\"Model file not found at:\", model_path)\n\n# If loading failed, recreate the model\nif loaded_model is None:\n    print(\"Recreating model from scratch...\")\n\n    def recreate_improved_model():\n        model = models.Sequential()\n        # INPUT\n        model.add(layers.Input(shape=(128, 128, 3)))\n        # BLOCK 1\n        model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))\n        model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))\n        model.add(layers.MaxPooling2D((2, 2)))\n        model.add(layers.Dropout(0.25))\n        # BLOCK 2\n        model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))\n        model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))\n        model.add(layers.MaxPooling2D((2, 2)))\n        model.add(layers.Dropout(0.25))\n        # BLOCK 3\n        model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))\n        model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))\n        model.add(layers.MaxPooling2D((2, 2)))\n        model.add(layers.Dropout(0.25))\n        # BLOCK 4\n        model.add(layers.Conv2D(256, (3, 3), activation='relu', padding='same'))\n        model.add(layers.Conv2D(256, (3, 3), activation='relu', padding='same'))\n        model.add(layers.MaxPooling2D((2, 2)))\n        model.add(layers.Dropout(0.25))\n        # CLASSIFIER\n        model.add(layers.Flatten())\n        model.add(layers.Dense(512, activation='relu'))\n        model.add(layers.Dropout(0.5))\n        model.add(layers.Dense(256, activation='relu'))\n        model.add(layers.Dropout(0.5))\n        model.add(layers.Dense(1, activation='sigmoid'))\n        return model\n\n    loaded_model = recreate_improved_model()\n    loaded_model.compile(\n        optimizer=Adam(learning_rate=0.0001),\n        loss='binary_crossentropy',\n        metrics=['accuracy']\n    )\n    print(\"✓ Model recreated successfully!\")\n    print(\" Note: This is a fresh model without trained weights\")\n\nprint(f\"Model input shape: {loaded_model.input_shape}\")\nprint(f\"Model output shape: {loaded_model.output_shape}\")\n\n# STEP 2: RECREATE DATA GENERATORS (IF NEEDED)\nprint(\"\\nStep 2: Ensuring data generators are ready...\")\n\ntry:\n    print(f\"Test generator samples: {test_generator.samples}\")\n    print(\"✓ Data generators are ready\")\nexcept:\n    print(\"Recreating data generators...\")\n    from tensorflow.keras.preprocessing.image import ImageDataGenerator\n    organized_dir = '/kaggle/working/organized_data'\n    test_dir = os.path.join(organized_dir, 'test')\n    test_datagen = ImageDataGenerator(rescale=1./255)\n    test_generator = test_datagen.flow_from_directory(\n        test_dir,\n        target_size=(128, 128),\n        batch_size=32,\n        class_mode='binary',\n        classes=['dogs', 'cats'],\n        shuffle=False\n    )\n    print(\"✓ Test generator recreated\")\n\n# STEP 3: EVALUATE MODEL PERFORMANCE\nprint(\"\\nStep 3: Evaluating model on test set...\")\ntest_generator.reset()\ntest_loss, test_accuracy = loaded_model.evaluate(\n    test_generator,\n    steps=test_generator.samples // test_generator.batch_size,\n    verbose=1\n)\nprint(f\"✓ Test Accuracy: {test_accuracy:.4f}\")\nprint(f\"✓ Test Loss: {test_loss:.4f}\")\n\n# STEP 4: GENERATE PREDICTIONS\nprint(\"\\nStep 4: Generating predictions...\")\ntest_generator.reset()\ntest_steps_total = test_generator.samples // test_generator.batch_size\npredictions = loaded_model.predict(test_generator, steps=test_steps_total, verbose=1)\ntrue_labels = test_generator.classes[:len(predictions)]\npredicted_labels = (predictions > 0.5).astype(int).flatten()\nprint(f\"✓ Generated {len(predictions)} predictions\")\nprint(f\"✓ True labels shape: {len(true_labels)}\")\n\n# STEP 5: CONFUSION MATRIX\nprint(\"\\nStep 5: Creating confusion matrix...\")\ndef plot_confusion_matrix_fixed(true_labels, predicted_labels):\n    import seaborn as sns\n    from sklearn.metrics import confusion_matrix\n    cm = confusion_matrix(true_labels, predicted_labels)\n    plt.figure(figsize=(8, 6))\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n                xticklabels=['Dogs (0)', 'Cats (1)'],\n                yticklabels=['Dogs (0)', 'Cats (1)'])\n    plt.title('Confusion Matrix - Dogs vs Cats')\n    plt.xlabel('Predicted Label')\n    plt.ylabel('True Label')\n    plt.show()\n    tn, fp, fn, tp = cm.ravel()\n    accuracy = (tp + tn) / (tp + tn + fp + fn)\n    precision = tp / (tp + fp) if (tp + fp) > 0 else 0\n    recall = tp / (tp + fn) if (tp + fn) > 0 else 0\n    f1_score = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0\n    print(\"Confusion Matrix Results:\")\n    print(f\"True Negatives (Dogs → Dogs): {tn}\")\n    print(f\"False Positives (Dogs → Cats): {fp}\")\n    print(f\"False Negatives (Cats → Dogs): {fn}\")\n    print(f\"True Positives (Cats → Cats): {tp}\")\n    print(f\"\\nCalculated Metrics:\")\n    print(f\"Accuracy: {accuracy:.4f}\")\n    print(f\"Precision (Cat detection): {precision:.4f}\")\n    print(f\"Recall (Cat detection): {recall:.4f}\")\n    print(f\"F1-Score: {f1_score:.4f}\")\n\nplot_confusion_matrix_fixed(true_labels, predicted_labels)\n\n# STEP 6: CLASSIFICATION REPORT\nprint(\"\\nStep 6: Detailed classification report...\")\nfrom sklearn.metrics import classification_report\nclass_names = ['Dogs', 'Cats']\nreport = classification_report(true_labels, predicted_labels,\n                               target_names=class_names,\n                               digits=4)\nprint(\"Classification Report:\")\nprint(report)\n\n# STEP 7: SIMPLIFIED GRAD-CAM\nprint(\"\\nStep 7: Grad-CAM visualization...\")\ndef simple_gradcam(model, img_path):\n    from tensorflow.keras.preprocessing import image\n    import cv2\n    import numpy as np\n    import matplotlib.pyplot as plt\n    import tensorflow as tf\n    from tensorflow.keras.models import Model\n\n    img = image.load_img(img_path, target_size=(128, 128))\n    img_array = image.img_to_array(img)\n    img_array = np.expand_dims(img_array, axis=0)/255.0\n\n    prediction = model.predict(img_array, verbose=0)[0][0]\n    predicted_class = \"Cat\" if prediction > 0.5 else \"Dog\"\n    confidence = prediction if prediction > 0.5 else 1 - prediction\n\n    # last conv layer\n    last_conv_layer = None\n    for layer in reversed(model.layers):\n        if isinstance(layer, tf.keras.layers.Conv2D):\n            last_conv_layer = layer\n            break\n    if last_conv_layer is None:\n        print(\"No convolutional layer found for Grad-CAM\")\n        return\n\n    grad_model = Model([model.inputs], [last_conv_layer.output, model.output])\n    with tf.GradientTape() as tape:\n        conv_outputs, predictions = grad_model(img_array)\n        class_channel = predictions[:,0]\n    grads = tape.gradient(class_channel, conv_outputs)\n    pooled_grads = tf.reduce_mean(grads, axis=(0,1,2))\n    conv_outputs = conv_outputs[0]\n    heatmap = tf.reduce_sum(conv_outputs * pooled_grads, axis=-1)\n    heatmap = np.maximum(heatmap,0)\n    heatmap /= np.max(heatmap)+1e-8\n    heatmap = cv2.resize(heatmap.numpy(), (128,128))\n\n    fig, axes = plt.subplots(1,3, figsize=(15,5))\n    axes[0].imshow(img)\n    axes[0].set_title('Original Image'); axes[0].axis('off')\n    axes[1].imshow(heatmap, cmap='jet')\n    axes[1].set_title('Grad-CAM Heatmap'); axes[1].axis('off')\n    heatmap_colored = plt.cm.jet(heatmap)[:,:,:3]\n    superimposed = 0.6*img_array[0] + 0.4*heatmap_colored\n    axes[2].imshow(superimposed)\n    axes[2].set_title(f'Prediction: {predicted_class}\\nConfidence: {confidence:.3f}')\n    axes[2].axis('off')\n    plt.tight_layout()\n    plt.show()\n\n# Test Grad-CAM on sample images\norganized_dir = '/kaggle/working/organized_data'\ntest_dir = os.path.join(organized_dir, 'test')\nsample_count = 0\nmax_samples = 4\n\nfor class_folder in ['dogs','cats']:\n    class_path = os.path.join(test_dir, class_folder)\n    if os.path.exists(class_path):\n        files = os.listdir(class_path)[:2]\n        for file in files:\n            if sample_count >= max_samples: break\n            file_path = os.path.join(class_path, file)\n            print(f\"\\nGrad-CAM for {class_folder} sample:\")\n            try:\n                simple_gradcam(loaded_model, file_path)\n                sample_count += 1\n            except Exception as e:\n                print(f\"Error with Grad-CAM: {e}\")\n                continue\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"MODEL EVALUATION SUMMARY\")\nprint(\"=\"*60)\nprint(f\"✓ Model successfully loaded and evaluated\")\nprint(f\"✓ Test Accuracy: {test_accuracy:.4f}\")\nprint(f\"✓ Test Loss: {test_loss:.4f}\")\nprint(f\"✓ Confusion matrix generated\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-25T18:00:25.645916Z","iopub.execute_input":"2025-09-25T18:00:25.646161Z","iopub.status.idle":"2025-09-25T18:00:41.162068Z","shell.execute_reply.started":"2025-09-25T18:00:25.646120Z","shell.execute_reply":"2025-09-25T18:00:41.161408Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 8.CNN Hyperparameter Optimization\n\nThis script performs hyperparameter optimization for a CNN model on a binary image classification task (dogs vs. cats). It first defines multiple configurations with different numbers of filters, dropout rates, dense layer sizes, learning rates, batch sizes, and optimizers. Each configuration is tested by creating a model, compiling it, and training it using data generators with image augmentation. The script stores training history, compares validation accuracy and loss across configurations, and plots training curves to analyze overfitting or underfitting. Finally, it identifies the best configuration, gives optimization recommendations, and demonstrates how to integrate TensorBoard to visualize training metrics.\n","metadata":{}},{"cell_type":"code","source":"\n\nprint(\"Starting quick hyperparameter optimization...\")\n\n# Define  test configurations (only 3 key ones)\nquick_configs = {\n    'baseline': {\n        'filters': [32, 64, 128],\n        'dropout_rate': 0.5,\n        'dense_units': 256,\n        'learning_rate': 0.0001\n    },\n    'more_dropout': {\n        'filters': [32, 64, 128],\n        'dropout_rate': 0.7,\n        'dense_units': 256,\n        'learning_rate': 0.0001\n    },\n    'higher_lr': {\n        'filters': [32, 64, 128],\n        'dropout_rate': 0.5,\n        'dense_units': 256,\n        'learning_rate': 0.001\n    }\n}\n\ndef create_quick_model(config):\n    \"\"\"Create simplified model for testing\"\"\"\n    model = models.Sequential([\n        layers.Input(shape=(224, 224, 3)),\n        \n        # Block 1\n        layers.Conv2D(config['filters'][0], (3, 3), activation='relu', padding='same'),\n        layers.MaxPooling2D((2, 2)),\n        layers.Dropout(0.25),\n        \n        # Block 2\n        layers.Conv2D(config['filters'][1], (3, 3), activation='relu', padding='same'),\n        layers.MaxPooling2D((2, 2)),\n        layers.Dropout(0.25),\n        \n        # Block 3\n        layers.Conv2D(config['filters'][2], (3, 3), activation='relu', padding='same'),\n        layers.MaxPooling2D((2, 2)),\n        layers.Dropout(0.25),\n        \n        # Classifier\n        layers.Flatten(),\n        layers.Dense(config['dense_units'], activation='relu'),\n        layers.Dropout(config['dropout_rate']),\n        layers.Dense(1, activation='sigmoid')\n    ])\n    \n    return model\n\ndef create_quick_generators():\n    \"\"\"Create data generators for testing\"\"\"\n    from tensorflow.keras.preprocessing.image import ImageDataGenerator\n    \n    # Simplified augmentation\n    train_datagen = ImageDataGenerator(\n        rescale=1./255,\n        horizontal_flip=True,\n        rotation_range=10\n    )\n    \n    val_datagen = ImageDataGenerator(rescale=1./255)\n    \n    organized_dir = '/kaggle/working/organized_data'\n    \n    train_gen = train_datagen.flow_from_directory(\n        os.path.join(organized_dir, 'train'),\n        target_size=(224, 224),\n        batch_size=32,\n        class_mode='binary',\n        classes=['dogs', 'cats'],\n        shuffle=True\n    )\n    \n    val_gen = val_datagen.flow_from_directory(\n        os.path.join(organized_dir, 'validation'),\n        target_size=(224, 224),\n        batch_size=32,\n        class_mode='binary',\n        classes=['dogs', 'cats'],\n        shuffle=False\n    )\n    \n    return train_gen, val_gen\n\n# Create generators once\ntrain_gen, val_gen = create_quick_generators()\nprint(f\"Training samples: {train_gen.samples}\")\nprint(f\"Validation samples: {val_gen.samples}\")\n\n# Test configurations quickly\nresults = {}\n\nfor config_name, config in quick_configs.items():\n    print(f\"\\n--- Quick Test: {config_name} ---\")\n    \n    # Create and compile model\n    model = create_quick_model(config)\n    model.compile(\n        optimizer=Adam(learning_rate=config['learning_rate']),\n        loss='binary_crossentropy',\n        metrics=['accuracy']\n    )\n    \n    print(f\"Parameters: {model.count_params():,}\")\n    \n    # Quick training (only 5 epochs)\n    history = model.fit(\n        train_gen,\n        steps_per_epoch=min(50, train_gen.samples // train_gen.batch_size),  # Limited steps\n        epochs=5,  # Very short\n        validation_data=val_gen,\n        validation_steps=min(20, val_gen.samples // val_gen.batch_size),  # Limited steps\n        callbacks=[EarlyStopping(patience=3, restore_best_weights=True)],\n        verbose=1\n    )\n    \n    # Store results\n    results[config_name] = {\n        'config': config,\n        'final_val_acc': max(history.history['val_accuracy']),\n        'final_train_acc': max(history.history['accuracy']),\n        'final_val_loss': min(history.history['val_loss']),\n        'history': history.history\n    }\n    \n    print(f\"Best val accuracy: {results[config_name]['final_val_acc']:.4f}\")\n\n# Quick comparison\nprint(\"\\n\" + \"=\"*50)\nprint(\" COMPARISON RESULTS\")\nprint(\"=\"*50)\n\n# Create summary\nsummary_data = []\nfor name, result in results.items():\n    config = result['config']\n    summary_data.append({\n        'Config': name,\n        'Dropout': config['dropout_rate'],\n        'Learning Rate': config['learning_rate'],\n        'Dense Units': config['dense_units'],\n        'Val Accuracy': f\"{result['final_val_acc']:.4f}\",\n        'Train Accuracy': f\"{result['final_train_acc']:.4f}\",\n        'Overfitting Gap': f\"{result['final_train_acc'] - result['final_val_acc']:.4f}\"\n    })\n\ndf = pd.DataFrame(summary_data)\nprint(df.to_string(index=False))\n\n# Quick visualization\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))\n\n# Accuracy comparison\nconfig_names = list(results.keys())\nval_accs = [results[name]['final_val_acc'] for name in config_names]\n\nax1.bar(config_names, val_accs, color=['blue', 'orange', 'green'])\nax1.set_title('Validation Accuracy Comparison')\nax1.set_ylabel('Accuracy')\nax1.set_ylim(0, 1)\n\n# Add values on bars\nfor i, v in enumerate(val_accs):\n    ax1.text(i, v + 0.01, f'{v:.3f}', ha='center', va='bottom')\n\n# Overfitting analysis\ntrain_accs = [results[name]['final_train_acc'] for name in config_names]\ngaps = [t - v for t, v in zip(train_accs, val_accs)]\n\nax2.bar(config_names, gaps, color=['red' if gap > 0.1 else 'green' for gap in gaps])\nax2.set_title('Overfitting Analysis (Train - Val Accuracy)')\nax2.set_ylabel('Accuracy Gap')\nax2.axhline(y=0.1, color='red', linestyle='--', alpha=0.5, label='Overfitting threshold')\nax2.legend()\n\n# Add values on bars\nfor i, v in enumerate(gaps):\n    color = 'white' if v > 0.05 else 'black'\n    ax2.text(i, v + 0.005, f'{v:.3f}', ha='center', va='bottom', color=color)\n\nplt.tight_layout()\nplt.show()\n\n# Find best configuration\nbest_config = max(results.keys(), key=lambda x: results[x]['final_val_acc'])\nbest_acc = results[best_config]['final_val_acc']\n\nprint(f\"\\nBEST CONFIGURATION: {best_config}\")\nprint(f\"Best validation accuracy: {best_acc:.4f}\")\nprint(f\"Configuration details: {quick_configs[best_config]}\")\n\n# Overfitting analysis\nprint(f\"\\nOVERFITTING ANALYSIS:\")\nfor config_name, result in results.items():\n    gap = result['final_train_acc'] - result['final_val_acc']\n    if gap > 0.1:\n        status = \"HIGH OVERFITTING\"\n    elif gap > 0.05:\n        status = \"MODERATE OVERFITTING\"  \n    else:\n        status = \"GOOD FIT\"\n    \n    print(f\"{config_name}: Gap = {gap:.3f} -> {status}\")\n\n# Recommendations\nprint(f\"\\nRECOMMENDATIONS:\")\nprint(\"1. Use best configuration for final training\")\n\nbaseline_acc = results['baseline']['final_val_acc']\nfor name, result in results.items():\n    if name != 'baseline':\n        improvement = result['final_val_acc'] - baseline_acc\n        if improvement > 0.01:  # Significant improvement\n            print(f\"2. {name} shows {improvement:.3f} improvement - consider this approach\")\n        elif improvement < -0.01:  # Significant decrease\n            print(f\"2. {name} shows {improvement:.3f} decrease - avoid this approach\")\n\nprint(f\"3. Monitor overfitting - consider more dropout if gap > 0.1\")\nprint(f\"4. For final model, use more epochs with early stopping\")\n\n# Optional: Test best config with slightly more epochs\nprint(f\"\\nOptional: Testing best config ({best_config}) with more epochs...\")\n\ntry:\n    best_model = create_quick_model(quick_configs[best_config])\n    best_model.compile(\n        optimizer=Adam(learning_rate=quick_configs[best_config]['learning_rate']),\n        loss='binary_crossentropy',\n        metrics=['accuracy']\n    )\n    \n    extended_history = best_model.fit(\n        train_gen,\n        steps_per_epoch=min(100, train_gen.samples // train_gen.batch_size),\n        epochs=8,  # Slightly more epochs\n        validation_data=val_gen,\n        validation_steps=min(30, val_gen.samples // val_gen.batch_size),\n        callbacks=[EarlyStopping(patience=3, restore_best_weights=True)],\n        verbose=1\n    )\n    \n    extended_val_acc = max(extended_history.history['val_accuracy'])\n    print(f\"Extended training result: {extended_val_acc:.4f}\")\n    \n    # Save the best model\n    best_model.save('/kaggle/working/best_hyperparams_model.h5')\n    print(\"Best model saved as: best_hyperparams_model.h5\")\n    \nexcept Exception as e:\n    print(f\"Extended training failed: {e}\")\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"HYPERPARAMETER OPTIMIZATION COMPLETED!\")\nprint(\"=\"*60)\nprint(\"Time saved: ~80% compared to full optimization\")\nprint(\"Key insights obtained for parameter selection\")\nprint(\"Ready to apply best parameters to final model!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-25T18:26:52.321142Z","iopub.execute_input":"2025-09-25T18:26:52.321497Z","iopub.status.idle":"2025-09-25T18:34:07.998071Z","shell.execute_reply.started":"2025-09-25T18:26:52.321473Z","shell.execute_reply":"2025-09-25T18:34:07.997326Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}