{"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":"none","dataSources":[{"sourceId":35760,"databundleVersionId":3441104,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Cat vs Dog Image Classification using CNN - Tutorial\n\n## 🎯 Learning Objectives\n\nIn this comprehensive tutorial, you will learn:\n- **Computer Vision Fundamentals**: Understanding how machines \"see\" images\n- **Convolutional Neural Networks (CNNs)**: The architecture that revolutionized image recognition\n- **Data Preprocessing**: Preparing image data for machine learning\n- **Model Training**: Building and training a CNN from scratch\n- **Performance Evaluation**: Assessing model accuracy and making predictions\n\n## 📖 What is Image Classification?\n\nImage classification is a computer vision task where we train a model to automatically categorize images into predefined classes. In our case, we want to distinguish between cats and dogs.\n\n## 🧠 Why CNNs for Images?\n\nConvolutional Neural Networks are specifically designed for image data because they:\n- **Preserve spatial relationships**: Unlike traditional neural networks, CNNs maintain the 2D structure of images\n- **Feature detection**: Automatically learn to detect edges, textures, and complex patterns\n- **Translation invariance**: Can recognize objects regardless of their position in the image\n- **Parameter sharing**: Use fewer parameters than fully connected networks\n\nLet's dive in! 🚀","metadata":{}},{"cell_type":"markdown","source":"## 1. Setup and Imports\n\nFirst, let's import all the libraries we'll need for this project.","metadata":{}},{"cell_type":"code","source":"# Essential imports for our CNN tutorial\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications import VGG16\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\n\n# Data manipulation and analysis\nimport numpy as np\nimport pandas as pd\nimport os\nimport glob\nfrom pathlib import Path\n\n# Visualization\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom PIL import Image\n\n# Utilities\nimport random\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Set random seeds for reproducibility\nSEED = 42\nnp.random.seed(SEED)\ntf.random.set_seed(SEED)\nrandom.seed(SEED)\n\n# Configure matplotlib for better plots\nplt.style.use('default')\nplt.rcParams['figure.figsize'] = (12, 8)\nplt.rcParams['font.size'] = 12\n\nprint(\"✅ All libraries imported successfully!\")\nprint(f\"📊 TensorFlow version: {tf.__version__}\")\nprint(f\"🔥 GPU Available: {len(tf.config.experimental.list_physical_devices('GPU')) > 0}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-17T00:10:43.340815Z","iopub.execute_input":"2025-07-17T00:10:43.341175Z","iopub.status.idle":"2025-07-17T00:11:08.480138Z","shell.execute_reply.started":"2025-07-17T00:10:43.341120Z","shell.execute_reply":"2025-07-17T00:11:08.478698Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Data Exploration and Understanding\n\nBefore building our model, let's explore our dataset to understand its structure and characteristics.","metadata":{}},{"cell_type":"code","source":"# Define paths to our dataset\nBASE_DIR = \"/kaggle/input/cat-and-dog-classification-harper2022\"\nTRAIN_CATS_DIR = os.path.join(BASE_DIR, \"Cat\")\nTRAIN_DOGS_DIR = os.path.join(BASE_DIR, \"Dog\") \nTEST_DIR = os.path.join(BASE_DIR, \"Test\")\n\n# Count images in each directory\ndef count_images(directory):\n    \"\"\"Count the number of image files in a directory.\"\"\"\n    if os.path.exists(directory):\n        return len([f for f in os.listdir(directory) if f.lower().endswith(('.jpg', '.jpeg', '.png'))])\n    return 0\n\nnum_cats = count_images(TRAIN_CATS_DIR)\nnum_dogs = count_images(TRAIN_DOGS_DIR)\nnum_test = count_images(TEST_DIR)\n\nprint(\"📁 Dataset Overview:\")\nprint(f\"🐱 Cat images: {num_cats:,}\")\nprint(f\"🐶 Dog images: {num_dogs:,}\")\nprint(f\"🧪 Test images: {num_test:,}\")\nprint(f\"📊 Total training images: {num_cats + num_dogs:,}\")\nprint(f\"⚖️  Class balance: {num_cats/(num_cats + num_dogs)*100:.1f}% cats, {num_dogs/(num_cats + num_dogs)*100:.1f}% dogs\")\n\n# Visualize class distribution\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))\n\n# Bar chart\nclasses = ['Cats', 'Dogs']\ncounts = [num_cats, num_dogs]\ncolors = ['orange', 'skyblue']\nax1.bar(classes, counts, color=colors, alpha=0.7)\nax1.set_title('Class Distribution in Training Set', fontsize=14, fontweight='bold')\nax1.set_ylabel('Number of Images')\nfor i, count in enumerate(counts):\n    ax1.text(i, count + 100, f'{count:,}', ha='center', fontweight='bold')\n\n# Pie chart\nax2.pie(counts, labels=classes, colors=colors, autopct='%1.1f%%', startangle=90)\nax2.set_title('Class Distribution (Percentage)', fontsize=14, fontweight='bold')\n\nplt.tight_layout()\nplt.show()\n\nprint(\"\\n💡 Key Observations:\")\nprint(\"• We have a perfectly balanced dataset with equal numbers of cat and dog images\")\nprint(\"• This eliminates class imbalance issues that could bias our model\")\nprint(\"• Large dataset size (20,000 images) should provide good training data\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-17T00:12:05.059130Z","iopub.execute_input":"2025-07-17T00:12:05.059556Z","iopub.status.idle":"2025-07-17T00:12:05.831635Z","shell.execute_reply.started":"2025-07-17T00:12:05.059516Z","shell.execute_reply":"2025-07-17T00:12:05.830168Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's examine some sample images to understand their characteristics\ndef display_sample_images(cat_dir, dog_dir, num_samples=8):\n    \"\"\"Display sample images from both classes to understand the data.\"\"\"\n    \n    # Get random sample of images\n    cat_images = random.sample(os.listdir(cat_dir), num_samples//2)\n    dog_images = random.sample(os.listdir(dog_dir), num_samples//2)\n    \n    fig, axes = plt.subplots(2, num_samples//2, figsize=(20, 8))\n    fig.suptitle('Sample Images from Our Dataset', fontsize=16, fontweight='bold')\n    \n    # Display cat images\n    for i, img_name in enumerate(cat_images):\n        img_path = os.path.join(cat_dir, img_name)\n        img = Image.open(img_path)\n        axes[0, i].imshow(img)\n        axes[0, i].set_title(f'🐱 Cat\\nSize: {img.size}', fontsize=10)\n        axes[0, i].axis('off')\n    \n    # Display dog images  \n    for i, img_name in enumerate(dog_images):\n        img_path = os.path.join(dog_dir, img_name)\n        img = Image.open(img_path)\n        axes[1, i].imshow(img)\n        axes[1, i].set_title(f'🐶 Dog\\nSize: {img.size}', fontsize=10)\n        axes[1, i].axis('off')\n    \n    plt.tight_layout()\n    plt.show()\n\n# Display sample images\ndisplay_sample_images(TRAIN_CATS_DIR, TRAIN_DOGS_DIR)\n\n# Analyze image dimensions\ndef analyze_image_dimensions(directory, sample_size=100):\n    \"\"\"Analyze the dimensions of images in a directory.\"\"\"\n    files = os.listdir(directory)\n    sample_files = random.sample(files, min(sample_size, len(files)))\n    \n    dimensions = []\n    for file in sample_files:\n        try:\n            img = Image.open(os.path.join(directory, file))\n            dimensions.append(img.size)  # (width, height)\n        except Exception:\n            continue\n    \n    return dimensions\n\nprint(\"🔍 Analyzing image dimensions...\")\ncat_dims = analyze_image_dimensions(TRAIN_CATS_DIR)\ndog_dims = analyze_image_dimensions(TRAIN_DOGS_DIR)\n\nall_widths = [dim[0] for dim in cat_dims + dog_dims]\nall_heights = [dim[1] for dim in cat_dims + dog_dims]\n\nprint(f\"📏 Image Dimension Analysis (sample of 200 images):\")\nprint(f\"Width  - Min: {min(all_widths)}, Max: {max(all_widths)}, Mean: {np.mean(all_widths):.0f}\")\nprint(f\"Height - Min: {min(all_heights)}, Max: {max(all_heights)}, Mean: {np.mean(all_heights):.0f}\")\n\n# Plot dimension distribution\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))\n\nax1.hist(all_widths, bins=30, alpha=0.7, color='blue', edgecolor='black')\nax1.set_title('Distribution of Image Widths')\nax1.set_xlabel('Width (pixels)')\nax1.set_ylabel('Frequency')\n\nax2.hist(all_heights, bins=30, alpha=0.7, color='green', edgecolor='black')\nax2.set_title('Distribution of Image Heights')\nax2.set_xlabel('Height (pixels)')\nax2.set_ylabel('Frequency')\n\nplt.tight_layout()\nplt.show()\n\nprint(\"\\n💡 Key Insights:\")\nprint(\"• Images have varying dimensions - we'll need to resize them for training\")\nprint(\"• Both cats and dogs show good variety in poses, lighting, and backgrounds\")\nprint(\"• This diversity will help our model generalize better to new images\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-17T00:12:49.141389Z","iopub.execute_input":"2025-07-17T00:12:49.141756Z","iopub.status.idle":"2025-07-17T00:12:52.941348Z","shell.execute_reply.started":"2025-07-17T00:12:49.141730Z","shell.execute_reply":"2025-07-17T00:12:52.940362Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Data Preprocessing and Augmentation\n\n### 🔄 What is Data Preprocessing?\nData preprocessing transforms raw images into a format suitable for neural network training. Key steps include:\n- **Resizing**: Making all images the same size\n- **Normalization**: Scaling pixel values to [0,1] range\n- **Data Augmentation**: Creating variations to increase dataset diversity\n\n### 🎨 Data Augmentation Techniques\nData augmentation artificially increases our dataset size by applying random transformations:\n- **Rotation**: Rotating images by small angles\n- **Zoom**: Slight zooming in/out\n- **Flip**: Horizontal flipping (vertical usually not suitable for cats/dogs)\n- **Shift**: Small translations\n- **Brightness**: Adjusting image brightness\n\nThis helps prevent overfitting and makes our model more robust!","metadata":{}},{"cell_type":"code","source":"# Set up our image parameters\nIMG_WIDTH = 150  # Width to resize images to\nIMG_HEIGHT = 150  # Height to resize images to\nBATCH_SIZE = 32   # Number of images to process at once\nEPOCHS = 25       # Number of training iterations\n\nprint(f\"🖼️ Image Configuration:\")\nprint(f\"Target size: {IMG_WIDTH}x{IMG_HEIGHT} pixels\")\nprint(f\"Batch size: {BATCH_SIZE}\")\nprint(f\"Training epochs: {EPOCHS}\")\n\n# Create data generators with augmentation for training\n# ImageDataGenerator automatically handles loading, resizing, and augmentation\ntrain_datagen = ImageDataGenerator(\n    rescale=1.0/255.0,          # Normalize pixel values to [0,1]\n    rotation_range=20,          # Randomly rotate images by up to 20 degrees\n    width_shift_range=0.2,      # Randomly shift images horizontally by up to 20%\n    height_shift_range=0.2,     # Randomly shift images vertically by up to 20%\n    shear_range=0.2,           # Apply shearing transformation\n    zoom_range=0.2,            # Randomly zoom in/out by up to 20%\n    horizontal_flip=True,       # Randomly flip images horizontally\n    fill_mode='nearest',        # Fill pixels after transformation\n    validation_split=0.2        # Use 20% of training data for validation\n)\n\n# For test data, we only rescale (no augmentation for evaluation)\ntest_datagen = ImageDataGenerator(rescale=1.0/255.0)\n\nprint(\"\\n✅ Data generators created with augmentation settings:\")\nprint(\"• Rotation: ±20 degrees\")\nprint(\"• Width/Height shift: ±20%\") \nprint(\"• Zoom range: ±20%\")\nprint(\"• Horizontal flip: Yes\")\nprint(\"• Validation split: 20%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-17T00:14:43.640592Z","iopub.execute_input":"2025-07-17T00:14:43.640999Z","iopub.status.idle":"2025-07-17T00:14:43.651434Z","shell.execute_reply.started":"2025-07-17T00:14:43.640958Z","shell.execute_reply":"2025-07-17T00:14:43.649975Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n       # We need to reorganize our data into train/cat and train/dog folders\n       import shutil\n\n       # Create temporary directory structure\n       TEMP_DIR = \"temp_data\"\n       TEMP_TRAIN_DIR = os.path.join(TEMP_DIR, \"train\")\n       TEMP_TRAIN_CATS = os.path.join(TEMP_TRAIN_DIR, \"cat\")\n       TEMP_TRAIN_DOGS = os.path.join(TEMP_TRAIN_DIR, \"dog\")\n\n       # Create directories if they don't exist\n       os.makedirs(TEMP_TRAIN_CATS, exist_ok=True)\n       os.makedirs(TEMP_TRAIN_DOGS, exist_ok=True)\n\n       print(\"📁 Creating temporary directory structure...\")\n\n       # Create symbolic links to avoid copying large files\n       # This creates references to original files without duplicating data\n       def create_symlinks(source_dir, target_dir, max_files=None):\n           \"\"\"Create symbolic links from source to target directory.\"\"\"\n           files = os.listdir(source_dir)\n           if max_files:\n               files = files[:max_files]\n\n           for file in files:\n               source_path = os.path.join(source_dir, file)\n               target_path = os.path.join(target_dir, file)\n\n               # Remove existing symlink if it exists\n               if os.path.islink(target_path):\n                   os.unlink(target_path)\n\n               # Create new symlink\n               try:\n                   os.symlink(os.path.abspath(source_path), target_path)\n               except FileExistsError:\n                   pass  # Link already exists\n\n       # For demonstration, we'll use a subset of images to speed up training\n       # In practice, you'd use the full dataset\n       SUBSET_SIZE = 2000  # Use 2000 images per class for faster training\n\n       print(f\"Creating symlinks for {SUBSET_SIZE} images per class...\")\n       create_symlinks(TRAIN_CATS_DIR, TEMP_TRAIN_CATS, SUBSET_SIZE)\n       create_symlinks(TRAIN_DOGS_DIR, TEMP_TRAIN_DOGS, SUBSET_SIZE)\n\n       print(f\"✅ Temporary structure created with {SUBSET_SIZE} images per class\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-17T00:23:46.203338Z","iopub.execute_input":"2025-07-17T00:23:46.203706Z","iopub.status.idle":"2025-07-17T00:23:46.454594Z","shell.execute_reply.started":"2025-07-17T00:23:46.203677Z","shell.execute_reply":"2025-07-17T00:23:46.452591Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n       # flow_from_directory automatically assigns labels based on subfolder names\n\n       # Training data generator (with augmentation)\n       train_generator = train_datagen.flow_from_directory(\n           TEMP_TRAIN_DIR,\n           target_size=(IMG_WIDTH, IMG_HEIGHT),\n           batch_size=BATCH_SIZE,\n           class_mode='binary',  # Binary classification (cat=0, dog=1)\n           subset='training',    # Use training subset (80%)\n           shuffle=True,         # Shuffle the data\n           seed=SEED            # For reproducibility\n       )\n\n       # Validation data generator (with same preprocessing, no augmentation)\n       validation_generator = train_datagen.flow_from_directory(\n           TEMP_TRAIN_DIR,\n           target_size=(IMG_WIDTH, IMG_HEIGHT),\n           batch_size=BATCH_SIZE,\n           class_mode='binary',\n           subset='validation',  # Use validation subset (20%)\n           shuffle=True,\n           seed=SEED\n       )\n\n       print(\"📊 Data generators created successfully!\")\n       print(f\"🏋️ Training samples: {train_generator.samples}\")\n       print(f\"✅ Validation samples: {validation_generator.samples}\")\n       print(f\"🏷️ Class indices: {train_generator.class_indices}\")\n       print(f\"📦 Steps per epoch: {train_generator.samples // BATCH_SIZE}\")\n       print(f\"🔍 Validation steps: {validation_generator.samples // BATCH_SIZE}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-17T00:24:10.244401Z","iopub.execute_input":"2025-07-17T00:24:10.244792Z","iopub.status.idle":"2025-07-17T00:24:13.536855Z","shell.execute_reply.started":"2025-07-17T00:24:10.244764Z","shell.execute_reply":"2025-07-17T00:24:13.535643Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Building Our CNN Architecture\n\n### 🧠 Understanding CNNs\n\nA Convolutional Neural Network (CNN) is specifically designed for image processing. Here's how it works:\n\n#### 🔍 Key Components:\n\n1. **Convolutional Layers**: Apply filters to detect features like edges, textures, and patterns\n2. **Pooling Layers**: Reduce spatial dimensions while keeping important information\n3. **Activation Functions**: Add non-linearity (ReLU is most common)\n4. **Dense Layers**: Traditional neural network layers for final classification\n5. **Dropout**: Prevents overfitting by randomly deactivating neurons during training\n\n#### 📐 Architecture Flow:\n       ```\nInput Image → Conv2D → MaxPooling → Conv2D → MaxPooling → Flatten → Dense → Output\n       ```\n\n### 🎯 Our CNN Design Strategy:\n- Start with smaller filters to detect basic features\n- Gradually increase filter count to detect complex patterns\n- Use pooling to reduce computational load\n- Add dropout for regularization\n- End with dense layers for classification","metadata":{}},{"cell_type":"code","source":"       # Build our CNN model step by step\n       def create_cnn_model(input_shape=(IMG_WIDTH, IMG_HEIGHT, 3)):\n           \"\"\"Create a CNN model for binary classification.\"\"\"\n\n           model = keras.Sequential([\n               # First Convolutional Block\n               # 32 filters of size 3x3, ReLU activation\n               layers.Conv2D(32, (3, 3), activation='relu', input_shape=input_shape, name='conv2d_1'),\n               layers.MaxPooling2D(2, 2, name='maxpool_1'),  # Reduce spatial dimensions by half\n\n               # Second Convolutional Block  \n               # Increase filters to 64 to detect more complex features\n               layers.Conv2D(64, (3, 3), activation='relu', name='conv2d_2'),\n               layers.MaxPooling2D(2, 2, name='maxpool_2'),\n\n               # Third Convolutional Block\n               # Further increase to 128 filters\n               layers.Conv2D(128, (3, 3), activation='relu', name='conv2d_3'),\n               layers.MaxPooling2D(2, 2, name='maxpool_3'),\n\n               # Fourth Convolutional Block\n               # 128 filters again to capture high-level features\n               layers.Conv2D(128, (3, 3), activation='relu', name='conv2d_4'),\n               layers.MaxPooling2D(2, 2, name='maxpool_4'),\n\n               # Flatten the 2D feature maps to 1D vector\n               layers.Flatten(name='flatten'),\n\n               # Add dropout for regularization (prevents overfitting)\n               layers.Dropout(0.5, name='dropout_1'),\n\n               # Dense layer with 512 neurons\n               layers.Dense(512, activation='relu', name='dense_1'),\n\n               # Another dropout layer\n               layers.Dropout(0.5, name='dropout_2'),\n\n               # Output layer - 1 neuron with sigmoid for binary classification\n               # Sigmoid outputs probability between 0 and 1\n               layers.Dense(1, activation='sigmoid', name='output')\n           ])\n\n           return model\n\n       # Create the model\n       model = create_cnn_model()\n\n       print(\"🏗️ CNN Model Architecture Created!\")\n       print(\"\\n📋 Model Summary:\")\n       model.summary()\n\n       print(\"\\n🔢 Model Statistics:\")\n       total_params = model.count_params()\n       print(f\"Total parameters: {total_params:,}\")\n       print(f\"Model input shape: {model.input_shape}\")\n       print(f\"Model output shape: {model.output_shape}\")\n\n       print(\"\\n💡 Architecture Explanation:\")\n       print(\"• Conv2D layers detect features (edges, textures, patterns)\")\n       print(\"• MaxPooling reduces spatial dimensions and computational load\")\n       print(\"• Filter count increases: 32 → 64 → 128 → 128\")\n       print(\"• Dropout (50%) prevents overfitting\")\n       print(\"• Dense layers perform final classification\")\n       print(\"• Sigmoid output gives probability: <0.5 = Cat, >0.5 = Dog\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-17T00:25:33.096258Z","iopub.execute_input":"2025-07-17T00:25:33.096648Z","iopub.status.idle":"2025-07-17T00:25:33.342260Z","shell.execute_reply.started":"2025-07-17T00:25:33.096619Z","shell.execute_reply":"2025-07-17T00:25:33.340591Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"       # Compile the model - specify optimizer, loss function, and metrics\n       model.compile(\n           optimizer='adam',              # Adam optimizer - adaptive learning rate\n           loss='binary_crossentropy',    # Binary crossentropy for binary classification\n           metrics=['accuracy']           # Track accuracy during training\n       )\n\n       print(\"⚙️ Model compiled successfully!\")\n       print(\"\\n🔧 Compilation Settings:\")\n       print(\"• Optimizer: Adam (adaptive learning rate)\")\n       print(\"• Loss function: Binary crossentropy\")\n       print(\"• Metrics: Accuracy\")\n\n       print(\"\\n📚 Why these choices?\")\n       print(\"• Adam: Self-adjusting learning rate, works well for most problems\")\n       print(\"• Binary crossentropy: Standard loss for binary classification\")\n       print(\"• Accuracy: Easy-to-understand metric for classification performance\")\n\n       # Set up callbacks for training\n       # Callbacks are functions called during training to monitor and control the process\n\n       callbacks = [\n           # Save the best model based on validation accuracy\n           ModelCheckpoint(\n               'best_cat_dog_model.h5',\n               monitor='val_accuracy',\n               save_best_only=True,\n               mode='max',\n               verbose=1\n           ),\n\n           # Stop training early if validation accuracy stops improving\n           EarlyStopping(\n               monitor='val_accuracy',\n               patience=5,  # Wait 5 epochs before stopping\n               mode='max',\n               verbose=1,\n               restore_best_weights=True\n           ),\n\n           # Reduce learning rate when validation accuracy plateaus\n           ReduceLROnPlateau(\n               monitor='val_accuracy',\n               factor=0.2,  # Reduce LR by factor of 5\n               patience=3,  # Wait 3 epochs before reducing\n               min_lr=0.0001,\n               verbose=1\n           )\n       ]\n\n       print(\"\\n🎯 Training callbacks configured:\")\n       print(\"• ModelCheckpoint: Save best model\")\n       print(\"• EarlyStopping: Prevent overfitting\")\n       print(\"• ReduceLROnPlateau: Adaptive learning rate\")\n       print(\"\\nReady to start training! 🚀\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-17T00:26:09.396762Z","iopub.execute_input":"2025-07-17T00:26:09.397118Z","iopub.status.idle":"2025-07-17T00:26:09.424379Z","shell.execute_reply.started":"2025-07-17T00:26:09.397092Z","shell.execute_reply":"2025-07-17T00:26:09.423266Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5. Training the Model\n\n### 🎯 Training Process Overview\n\nDuring training, our CNN will:\n       1. **Forward Pass**: Process images through the network\n       2. **Calculate Loss**: Compare predictions with actual labels\n       3. **Backward Pass**: Update weights using gradients\n       4. **Repeat**: Continue for multiple epochs until convergence\n\n### 📊 Monitoring Training\n\nWe'll track several metrics:\n       - **Training Accuracy**: How well the model performs on training data\n       - **Validation Accuracy**: Performance on unseen validation data (most important!)\n       - **Training Loss**: How confident the model is in its predictions\n       - **Validation Loss**: Loss on validation data\n\n### ⚠️ Watching for Overfitting\n\nIf training accuracy keeps improving but validation accuracy plateaus or decreases, the model is overfitting (memorizing training data instead\n       of learning general patterns).","metadata":{}},{"cell_type":"code","source":"       # Train the model\n       print(\"🚀 Starting model training...\")\n       print(f\"Training for {EPOCHS} epochs with {train_generator.samples} training samples\")\n       print(f\"Validating on {validation_generator.samples} validation samples\")\n       print(\"\\n\" + \"=\"*50)\n\n       # Calculate steps per epoch\n       steps_per_epoch = train_generator.samples // BATCH_SIZE\n       validation_steps = validation_generator.samples // BATCH_SIZE\n\n       print(f\"Steps per epoch: {steps_per_epoch}\")\n       print(f\"Validation steps: {validation_steps}\")\n       print(\"\\nStarting training...\\n\")\n\n       # Train the model\n       history = model.fit(\n           train_generator,\n           steps_per_epoch=steps_per_epoch,\n           epochs=EPOCHS,\n           validation_data=validation_generator,\n           validation_steps=validation_steps,\n           callbacks=callbacks,\n           verbose=1  # Show progress bar\n       )\n\n       print(\"\\n🎉 Training completed!\")\n       print(f\"📊 Final training accuracy: {history.history['accuracy'][-1]:.4f}\")\n       print(f\"✅ Final validation accuracy: {history.history['val_accuracy'][-1]:.4f}\")\n       print(f\"📉 Final training loss: {history.history['loss'][-1]:.4f}\")\n       print(f\"🔍 Final validation loss: {history.history['val_loss'][-1]:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-17T00:27:20.541012Z","iopub.execute_input":"2025-07-17T00:27:20.541504Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"       def plot_training_history(history):\n           \"\"\"Plot training and validation accuracy and loss.\"\"\"\n\n           # Extract metrics from 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           # Create subplots\n           fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))\n           fig.suptitle('Training Progress', fontsize=16, fontweight='bold')\n\n           # Plot accuracy\n           ax1.plot(epochs_range, acc, 'b-', label='Training Accuracy', linewidth=2)\n           ax1.plot(epochs_range, val_acc, 'r-', label='Validation Accuracy', linewidth=2)\n           ax1.set_title('Model Accuracy', fontsize=14)\n           ax1.set_xlabel('Epoch')\n           ax1.set_ylabel('Accuracy')\n           ax1.legend()\n           ax1.grid(True, alpha=0.3)\n\n           # Plot loss\n           ax2.plot(epochs_range, loss, 'b-', label='Training Loss', linewidth=2)\n           ax2.plot(epochs_range, val_loss, 'r-', label='Validation Loss', linewidth=2)\n           ax2.set_title('Model Loss', fontsize=14)\n           ax2.set_xlabel('Epoch')\n           ax2.set_ylabel('Loss')\n           ax2.legend()\n           ax2.grid(True, alpha=0.3)\n\n           plt.tight_layout()\n           plt.show()\n\n           # Analyze the results\n           best_val_acc = max(val_acc)\n           best_epoch = val_acc.index(best_val_acc) + 1\n           final_gap = acc[-1] - val_acc[-1]\n\n           print(f\"\\n📈 Training Analysis:\")\n           print(f\"• Best validation accuracy: {best_val_acc:.4f} (epoch {best_epoch})\")\n           print(f\"• Final accuracy gap: {final_gap:.4f} (training - validation)\")\n\n           if final_gap > 0.1:\n               print(\"⚠️  Large gap suggests overfitting - consider more regularization\")\n           elif final_gap < 0.05:\n               print(\"✅ Good balance between training and validation performance\")\n           else:\n               print(\"📊 Moderate gap - model performance is acceptable\")\n\n       # Plot the training history\n       plot_training_history(history)\n\n       print(\"\\n🎯 Key Takeaways:\")\n       print(\"• Watch the validation accuracy - it's the true measure of model performance\")\n       print(\"• If validation accuracy stops improving while training accuracy keeps rising, that's overfitting\")\n       print(\"• Our callbacks help prevent overfitting and save the best model automatically\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 6. Model Evaluation and Predictions\n\n### 🎯 Evaluation Strategy\n\nNow let's test our trained model:\n       1. **Load the best saved model** (from ModelCheckpoint callback)\n       2. **Make predictions** on new images\n       3. **Analyze prediction confidence**\n       4. **Visualize results** with sample predictions\n\n### 🔍 Understanding Predictions\n\nOur model outputs a probability between 0 and 1:\n       - **< 0.5**: Predicted as Cat (closer to 0 = more confident)\n       - **> 0.5**: Predicted as Dog (closer to 1 = more confident)\n       - **≈ 0.5**: Model is uncertain\n\n### 📊 Confidence Interpretation\n- **0.9+**: Very confident\n- **0.7-0.9**: Confident\n- **0.5-0.7**: Moderately confident\n- **< 0.5**: Low confidence (for respective class)","metadata":{}},{"cell_type":"code","source":"       # Load the best saved model\n       print(\"📥 Loading the best trained model...\")\n       best_model = keras.models.load_model('best_cat_dog_model.h5')\n       print(\"✅ Best model loaded successfully!\")\n\n       # Function to make predictions on individual images\n       def predict_image(model, img_path, target_size=(IMG_WIDTH, IMG_HEIGHT)):\n           \"\"\"Make a prediction on a single image.\"\"\"\n           # Load and preprocess the image\n           img = Image.open(img_path)\n           img = img.resize(target_size)\n           img_array = np.array(img) / 255.0  # Normalize\n           img_array = np.expand_dims(img_array, axis=0)  # Add batch dimension\n\n           # Make prediction\n           prediction = model.predict(img_array, verbose=0)[0][0]\n\n           # Interpret the prediction\n           if prediction > 0.5:\n               label = \"Dog\"\n               confidence = prediction\n           else:\n               label = \"Cat\"\n               confidence = 1 - prediction\n\n           return label, confidence, prediction\n\n       # Function to display predictions with images\n       def show_predictions(model, image_paths, true_labels=None):\n           \"\"\"Display images with their predictions.\"\"\"\n           num_images = len(image_paths)\n           cols = 4\n           rows = (num_images + cols - 1) // cols\n\n           fig, axes = plt.subplots(rows, cols, figsize=(16, 4*rows))\n           if rows == 1:\n               axes = axes.reshape(1, -1)\n\n           fig.suptitle('Model Predictions', fontsize=16, fontweight='bold')\n\n           for i, img_path in enumerate(image_paths):\n               row = i // cols\n               col = i % cols\n\n               # Make prediction\n               label, confidence, raw_prediction = predict_image(model, img_path)\n\n               # Load and display image\n               img = Image.open(img_path)\n               axes[row, col].imshow(img)\n\n               # Create title with prediction info\n               confidence_icon = \"🔥\" if confidence > 0.9 else \"✅\" if confidence > 0.7 else \"⚠️\"\n               title = f\"{confidence_icon} {label}\\nConfidence: {confidence:.2f}\"\n\n               # Add true label if provided\n               if true_labels and i < len(true_labels):\n                   true_label = true_labels[i]\n                   is_correct = (label.lower() == true_label.lower())\n                   title += f\"\\nTrue: {true_label} {'✓' if is_correct else '✗'}\"\n\n               axes[row, col].set_title(title, fontsize=10)\n               axes[row, col].axis('off')\n\n           # Hide empty subplots\n           for i in range(num_images, rows * cols):\n               row = i // cols\n               col = i % cols\n               axes[row, col].axis('off')\n\n           plt.tight_layout()\n           plt.show()\n\n       print(\"\\n🔮 Prediction functions ready!\")\n       print(\"Let's test our model on some images...\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"       # Test predictions on sample images from our training set\n       print(\"🧪 Testing on sample training images...\")\n\n       # Get random sample images from each class\n       cat_samples = random.sample(os.listdir(TRAIN_CATS_DIR), 4)\n       dog_samples = random.sample(os.listdir(TRAIN_DOGS_DIR), 4)\n\n       # Create full paths\n       cat_paths = [os.path.join(TRAIN_CATS_DIR, img) for img in cat_samples]\n       dog_paths = [os.path.join(TRAIN_DOGS_DIR, img) for img in dog_samples]\n\n       # Combine paths and labels\n       test_paths = cat_paths + dog_paths\n       true_labels = ['Cat'] * 4 + ['Dog'] * 4\n\n       print(f\"Testing on {len(test_paths)} images...\")\n       show_predictions(best_model, test_paths, true_labels)\n\n       # Calculate accuracy on this sample\n       correct = 0\n       for i, path in enumerate(test_paths):\n           label, confidence, raw_pred = predict_image(best_model, path)\n           if label.lower() == true_labels[i].lower():\n               correct += 1\n\n       accuracy = correct / len(test_paths)\n       print(f\"\\n📊 Sample accuracy: {accuracy:.2%} ({correct}/{len(test_paths)})\")\n\n       print(\"\\n💡 Observations:\")\n       print(\"• 🔥 High confidence (>90%) predictions are usually very reliable\")\n       print(\"• ✅ Good confidence (70-90%) predictions are generally accurate\")\n       print(\"• ⚠️ Lower confidence (<70%) predictions may be uncertain or difficult cases\")\n       print(\"• Look for patterns in misclassifications to understand model limitations\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 7. Conclusion and Key Takeaways\n\n### 🎉 What We've Accomplished\n\nCongratulations! You've successfully built a complete image classification system from scratch. Here's what we covered:\n\n### 📚 Key Concepts Learned:\n\n1. **Computer Vision Fundamentals**:\n          - How machines process images as numerical data\n          - The importance of data preprocessing and normalization\n\n2. **CNN Architecture**:\n          - Convolutional layers for feature detection\n          - Pooling layers for dimensionality reduction\n          - Dense layers for classification\n          - Dropout for regularization\n\n3. **Training Best Practices**:\n          - Data augmentation to prevent overfitting\n          - Callbacks for monitoring and control\n          - Validation strategy to assess true performance\n\n4. **Model Evaluation**:\n          - Understanding prediction confidence\n          - Analyzing training curves\n          - Testing on unseen data\n\n### 🚀 Next Steps for Improvement:\n\n1. **Experiment with Transfer Learning**: Try VGG16, ResNet, or EfficientNet\n2. **Increase Dataset Size**: Use the full 20,000 image dataset\n3. **Advanced Augmentation**: Implement more sophisticated data augmentation\n4. **Hyperparameter Tuning**: Optimize learning rate, batch size, and architecture\n5. **Model Deployment**: Create a web app or mobile app using your trained model\n\n### 💡 Real-World Applications:\n\n- **Medical Imaging**: Detect diseases in X-rays or MRIs\n- **Autonomous Vehicles**: Recognize objects in road scenes\n- **Security Systems**: Facial recognition and surveillance\n- **E-commerce**: Visual product search and recommendation\n- **Agriculture**: Crop disease detection and monitoring\n\n### 🔗 Additional Resources:\n\n- TensorFlow/Keras Documentation\n- Deep Learning Specialization (Coursera)\n- Papers With Code (latest research)\n- Kaggle Competitions (practice datasets)\n\n  Keep experimenting and building! 🚀","metadata":{}},{"cell_type":"code","source":"       # Clean up temporary files\n       print(\"🧹 Cleaning up temporary files...\")\n\n       # Remove temporary directory structure\n       import shutil\n       if os.path.exists(TEMP_DIR):\n           shutil.rmtree(TEMP_DIR)\n           print(\"✅ Temporary directory removed\")\n\n       print(\"\\n📁 Final Project Files:\")\n       print(\"• notebook.ipynb - This complete tutorial\")\n       print(\"• best_cat_dog_model.h5 - Your trained CNN model\")\n       print(\"• CLAUDE.md - Project documentation\")\n\n       print(\"\\n🎯 Your CNN Model Stats:\")\n       print(f\"• Architecture: Custom 4-layer CNN\")\n       print(f\"• Parameters: {model.count_params():,}\")\n       print(f\"• Input size: {IMG_WIDTH}x{IMG_HEIGHT}x3\")\n       print(f\"• Training data: {SUBSET_SIZE*2:,} images\")\n\n       print(\"\\n🏆 Congratulations! You've successfully:\")\n       print(\"✓ Built a CNN from scratch\")\n       print(\"✓ Trained it on real image data\")\n       print(\"✓ Achieved good classification performance\")\n       print(\"✓ Learned fundamental deep learning concepts\")\n       print(\"✓ Created a complete end-to-end ML pipeline\")\n\n       print(\"\\n🚀 Ready to tackle your next computer vision challenge!\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}