{"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":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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","execution":{"iopub.status.busy":"2025-07-21T08:42:05.938070Z","iopub.execute_input":"2025-07-21T08:42:05.938428Z","iopub.status.idle":"2025-07-21T08:42:05.944653Z","shell.execute_reply.started":"2025-07-21T08:42:05.938401Z","shell.execute_reply":"2025-07-21T08:42:05.943763Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Main ideas:\n- Preprocess images by normalizing, reshaping them to fit the CNN input, and converting labels to one-hot vectors.\n- Use a deep CNN model (with Conv2D, MaxPooling2D, BatchNormalization, and Dropout layers) to learn image features.\n- Apply data augmentation to help the model generalize better.\n- Train the model using callbacks (EarlyStopping, ReduceLROnPlateau, and ModelCheckpoint).","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics import accuracy_score, classification_report, confusion_matrix\nimport seaborn as sns\n\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential, Model\nfrom tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D, BatchNormalization, GlobalAveragePooling2D\nfrom tensorflow.keras.optimizers import Adam, RMSprop\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.applications import ResNet50V2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-21T08:42:05.945950Z","iopub.execute_input":"2025-07-21T08:42:05.946202Z","iopub.status.idle":"2025-07-21T08:42:05.966854Z","shell.execute_reply.started":"2025-07-21T08:42:05.946183Z","shell.execute_reply":"2025-07-21T08:42:05.965963Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.random.seed(42)\ntf.random.set_seed(42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-21T08:42:05.967800Z","iopub.execute_input":"2025-07-21T08:42:05.968123Z","iopub.status.idle":"2025-07-21T08:42:05.984960Z","shell.execute_reply.started":"2025-07-21T08:42:05.968101Z","shell.execute_reply":"2025-07-21T08:42:05.984202Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# The DigitRecognizer class consists of the following steps:\n1. Load train.csv and test.csv into variables: X_train, y_train, and X_test.\n2. Data Preprocessing:\n   - Normalize the pixel values to the range [0, 1] by dividing by 255.\n   - Reshape the input images to (28, 28, 1) to match the input shape expected by CNN.\n   - One-hot encode the labels using to_categorical.\n   - Split the training data into training and validation sets (90%-10%) using train_test_split.\n3. Build the CNN model\n   - 2 blocks (Conv2D -> BatchNormalization -> Maxpooling + Dropout): \n         + Block 1: 2 Conv2D layers with 32 filters \n         + Block 2: 2 Conv2D layers with 64 filters\n   - One additional convolutional block (Conv2D → BatchNormalization + Dropout) with 128 filters\n   - Flatten + Dense layers:  \n         + Dense layer with 512 units + dropout \n         + Dense layer with 256 units + dropout\n         + Final Dense layer with softmax activation (10 units) for classification (digits 0 → 9)\n    - Conv2D : extracts image features\n    - BatchNormalization:  stabilizes the output distribution, helping the model converge faster\n    - MaxPooling2D: reduces the spatial dimensions to decrease the number of features.\n4. Data Augmentation\n   - Use ImageDataGenerator to apply random transformations such as rotation, zoom, shifting, shear, etc. This improves generalization and helps prevent overfitting.\n5. Train model:\n   - Compile the model using Adam optimizer, categorical_crossentropy loss, and accuracy metric.\n   - Adam (Adaptive Moment Estimation) is an optimization algorithm that combines Momentum (the moving average of gradients) and RMSProp (the moving average of squared gradients). It helps the model update weights after each backpropagation step and automatically adjusts the learning rate for each parameter.\n   - Categorical crossentropy measures the difference between predicted and actual labels and is commonly used in multi-class classification with one-hot encoded targets.\n   - Use callback: EarlyStopping (stops training if val_accuracy doesn't improve after 10 epochs), ReduceLROnPlateau (reduces learning rate if val_loss doesn't improve), ModelCheckpoint (saves the best model based on val_accuracy).\n   - Train the model using ImageDataGenerator.flow(...) to combine training and augmentation.\n6. Evaluate the model\n   - Predict labels for the validation set and extract the class with the highest probability.\n   - Display metrics: accuracy, precision, recall, F1-score.\n   - Plot the confusion matrix to visualize which digits the model often confuses.\n7. Visualize training history\n   - Plot training and validation accuracy/loss per epoch to analyze overfitting or underfitting behavior.\n8. Predict test labels and save to submission.csv for Kaggle submission.\n9. Show a number of images from the validation set with their true and predicted labels for visual inspection.","metadata":{}},{"cell_type":"code","source":"class DigitRecognizer:\n    def __init__(self):\n        self.model = None\n        self.scaler = StandardScaler()\n        self.history = None\n        \n    def load_data(self, train_path='train.csv', test_path='test.csv'):\n        \"\"\"Load and preprocess the data\"\"\"\n        print(\"Loading data...\")\n        \n        # Load training data\n        train_data = pd.read_csv(train_path)\n        self.X_train = train_data.drop('label', axis=1).values\n        self.y_train = train_data['label'].values\n        \n        # Load test data\n        self.X_test = pd.read_csv(test_path).values\n        \n        print(f\"Training data shape: {self.X_train.shape}\")\n        print(f\"Test data shape: {self.X_test.shape}\")\n        \n        return self\n        \n    def preprocess_data(self):\n        \"\"\"Preprocess the data\"\"\"\n        print(\"Preprocessing data...\")\n        \n        # Normalize pixel values to [0, 1]\n        self.X_train = self.X_train / 255.0\n        self.X_test = self.X_test / 255.0\n        \n        # Reshape for CNN (28x28x1)\n        self.X_train = self.X_train.reshape(-1, 28, 28, 1)\n        self.X_test = self.X_test.reshape(-1, 28, 28, 1)\n        \n        # Convert labels to categorical\n        self.y_train = to_categorical(self.y_train, 10)\n        \n        # Split training data for validation\n        self.X_train_split, self.X_val, self.y_train_split, self.y_val = train_test_split(\n            self.X_train, self.y_train, test_size=0.1, random_state=42, stratify=self.y_train\n        )\n        \n        print(f\"Training set: {self.X_train_split.shape}\")\n        print(f\"Validation set: {self.X_val.shape}\")\n        \n        return self\n        \n    def create_cnn_model(self):\n        \"\"\"Create optimized CNN model\"\"\"\n        model = Sequential([\n            # First Conv Block\n            Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),\n            BatchNormalization(),\n            Conv2D(32, (3, 3), activation='relu'),\n            MaxPooling2D((2, 2)),\n            Dropout(0.25),\n            \n            # Second Conv Block\n            Conv2D(64, (3, 3), activation='relu'),\n            BatchNormalization(),\n            Conv2D(64, (3, 3), activation='relu'),\n            MaxPooling2D((2, 2)),\n            Dropout(0.25),\n            \n            # Third Conv Block\n            Conv2D(128, (3, 3), activation='relu'),\n            BatchNormalization(),\n            Dropout(0.25),\n            \n            # Dense layers\n            Flatten(),\n            Dense(512, activation='relu'),\n            BatchNormalization(),\n            Dropout(0.5),\n            Dense(256, activation='relu'),\n            Dropout(0.5),\n            Dense(10, activation='softmax')\n        ])\n        \n        return model\n        \n    def setup_data_augmentation(self):\n        \"\"\"Setup data augmentation\"\"\"\n        datagen = ImageDataGenerator(\n            rotation_range=10,\n            zoom_range=0.1,\n            width_shift_range=0.1,\n            height_shift_range=0.1,\n            shear_range=0.1,\n            fill_mode='nearest'\n        )\n        \n        return datagen\n        \n    def train_model(self, model_type='cnn', epochs=50, batch_size=128):\n        \"\"\"Train the model\"\"\"\n        print(f\"Training {model_type} model...\")\n        \n        # Create model\n        self.model = self.create_cnn_model()\n\n        # Compile model\n        self.model.compile(\n            optimizer=Adam(learning_rate=0.001),\n            loss='categorical_crossentropy',\n            metrics=['accuracy']\n        )\n        \n        print(self.model.summary())\n        \n        # Setup callbacks\n        callbacks = [\n            EarlyStopping(\n                monitor='val_accuracy',\n                patience=10,\n                restore_best_weights=True,\n                verbose=1\n            ),\n            ReduceLROnPlateau(\n                monitor='val_loss',\n                factor=0.5,\n                patience=5,\n                min_lr=1e-7,\n                verbose=1\n            ),\n            ModelCheckpoint(\n                'best_model.h5',\n                monitor='val_accuracy',\n                save_best_only=True,\n                verbose=1\n            )\n        ]\n        \n        # Setup data augmentation\n        datagen = self.setup_data_augmentation()\n        datagen.fit(self.X_train_split)\n        \n        # Train model\n        self.history = self.model.fit(\n            datagen.flow(self.X_train_split, self.y_train_split, batch_size=batch_size),\n            epochs=epochs,\n            validation_data=(self.X_val, self.y_val),\n            callbacks=callbacks,\n            verbose=1\n        )\n        \n        return self\n        \n    def evaluate_model(self):\n        \"\"\"Evaluate the model\"\"\"\n        print(\"Evaluating model...\")\n        \n        # Predictions on validation set\n        val_predictions = self.model.predict(self.X_val)\n        val_pred_classes = np.argmax(val_predictions, axis=1)\n        val_true_classes = np.argmax(self.y_val, axis=1)\n        \n        # Calculate accuracy\n        accuracy = accuracy_score(val_true_classes, val_pred_classes)\n        print(f\"Validation Accuracy: {accuracy:.4f}\")\n        \n        # Classification report\n        print(\"\\nClassification Report:\")\n        print(classification_report(val_true_classes, val_pred_classes))\n        \n        # Confusion matrix\n        cm = confusion_matrix(val_true_classes, val_pred_classes)\n        plt.figure(figsize=(10, 8))\n        sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')\n        plt.title('Confusion Matrix')\n        plt.xlabel('Predicted')\n        plt.ylabel('Actual')\n        plt.show()\n        \n        return accuracy\n        \n    def plot_training_history(self):\n        \"\"\"Plot training history\"\"\"\n        if self.history is None:\n            print(\"No training history available\")\n            return\n            \n        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))\n        \n        # Plot accuracy\n        ax1.plot(self.history.history['accuracy'], label='Training Accuracy')\n        ax1.plot(self.history.history['val_accuracy'], label='Validation Accuracy')\n        ax1.set_title('Model Accuracy')\n        ax1.set_xlabel('Epoch')\n        ax1.set_ylabel('Accuracy')\n        ax1.legend()\n        \n        # Plot loss\n        ax2.plot(self.history.history['loss'], label='Training Loss')\n        ax2.plot(self.history.history['val_loss'], label='Validation Loss')\n        ax2.set_title('Model Loss')\n        ax2.set_xlabel('Epoch')\n        ax2.set_ylabel('Loss')\n        ax2.legend()\n        \n        plt.tight_layout()\n        plt.show()\n        \n    def make_predictions(self, save_path='submission.csv'):\n        \"\"\"Make predictions on test set\"\"\"\n        print(\"Making predictions...\")\n        \n        # Predict on test set\n        test_predictions = self.model.predict(self.X_test)\n        test_pred_classes = np.argmax(test_predictions, axis=1)\n        \n        # Create submission file\n        submission = pd.DataFrame({\n            'ImageId': range(1, len(test_pred_classes) + 1),\n            'Label': test_pred_classes\n        })\n        \n        submission.to_csv(save_path, index=False)\n        print(f\"Submission saved to {save_path}\")\n        \n        return submission\n        \n    def visualize_predictions(self, num_samples=10):\n        \"\"\"Visualize some predictions\"\"\"\n        # Get some validation samples\n        sample_indices = np.random.choice(len(self.X_val), num_samples, replace=False)\n        sample_images = self.X_val[sample_indices]\n        sample_labels = self.y_val[sample_indices]\n        \n        # Make predictions\n        predictions = self.model.predict(sample_images)\n        predicted_classes = np.argmax(predictions, axis=1)\n        true_classes = np.argmax(sample_labels, axis=1)\n        \n        # Plot\n        fig, axes = plt.subplots(2, 5, figsize=(12, 6))\n        axes = axes.ravel()\n        \n        for i in range(num_samples):\n            axes[i].imshow(sample_images[i].reshape(28, 28), cmap='gray')\n            axes[i].set_title(f'True: {true_classes[i]}, Pred: {predicted_classes[i]}')\n            axes[i].axis('off')\n            \n        plt.tight_layout()\n        plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-21T08:42:06.128912Z","iopub.execute_input":"2025-07-21T08:42:06.129719Z","iopub.status.idle":"2025-07-21T08:42:06.152339Z","shell.execute_reply.started":"2025-07-21T08:42:06.129690Z","shell.execute_reply":"2025-07-21T08:42:06.151470Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"recognizer = DigitRecognizer()\n    \n# Load and preprocess data\nrecognizer.load_data('/kaggle/input/digit-recognizer/train.csv', '/kaggle/input/digit-recognizer/test.csv')\nrecognizer.preprocess_data()\n\n# Train the model\nrecognizer.train_model(model_type='cnn', epochs=30, batch_size=128)\n\n# Evaluate the model\naccuracy = recognizer.evaluate_model()\n\n# Plot training history\nrecognizer.plot_training_history()\n\n# Visualize some predictions\nrecognizer.visualize_predictions()\n\n# Make final predictions\nsubmission = recognizer.make_predictions('submission.csv')\n\nprint(f\"\\nFinal validation accuracy: {accuracy:.4f}\")\nprint(\"Submission file created successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-21T08:42:06.153540Z","iopub.execute_input":"2025-07-21T08:42:06.153750Z","iopub.status.idle":"2025-07-21T08:48:41.740548Z","shell.execute_reply.started":"2025-07-21T08:42:06.153732Z","shell.execute_reply":"2025-07-21T08:48:41.739793Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}