{"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":31089,"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","trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:57:45.118454Z","iopub.execute_input":"2025-08-20T09:57:45.118703Z","iopub.status.idle":"2025-08-20T09:57:47.178889Z","shell.execute_reply.started":"2025-08-20T09:57:45.118679Z","shell.execute_reply":"2025-08-20T09:57:47.178161Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom sklearn.model_selection import train_test_split\n\n# Load\ntrain_df = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')\ntest_df = pd.read_csv('/kaggle/input/digit-recognizer/test.csv')\n\n# Features and labels\nX = train_df.drop('label', axis=1).values  # (42000, 784)\ny = train_df['label'].values               # (42000,)\n\n# Normalize\nX = X.astype('float32') / 255.0\nX_test = test_df.values.astype('float32') / 255.0\n\n# Reshape for CNN\nX = X.reshape(-1, 28, 28, 1)\nX_test = X_test.reshape(-1, 28, 28, 1)\n\n# Train/val split\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.1, random_state=42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:57:47.180418Z","iopub.execute_input":"2025-08-20T09:57:47.180740Z","iopub.status.idle":"2025-08-20T09:58:07.432643Z","shell.execute_reply.started":"2025-08-20T09:57:47.180721Z","shell.execute_reply":"2025-08-20T09:58:07.431821Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:58:07.433440Z","iopub.execute_input":"2025-08-20T09:58:07.433669Z","iopub.status.idle":"2025-08-20T09:58:07.440266Z","shell.execute_reply.started":"2025-08-20T09:58:07.433653Z","shell.execute_reply":"2025-08-20T09:58:07.439347Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2 \n\ndef add_edges_batch(images):\n    edge_images = []\n    for img in images:\n        gray = img.squeeze()  # (28,28)\n        sobel = cv2.Sobel(gray, cv2.CV_64F, 1, 1, ksize=3)\n        sobel = np.abs(sobel) / np.max(sobel)  # Normalize\n        edge_images.append(sobel.reshape(28, 28, 1))\n    return np.array(edge_images)\n\nX_train_edges = add_edges_batch(X_train)\nX_val_edges = add_edges_batch(X_val)\n\n# Combine original + edges (2 channels)\nX_train_combined = np.concatenate([X_train, X_train_edges], axis=-1)  # (N,28,28,2)\nX_val_combined = np.concatenate([X_val, X_val_edges], axis=-1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:58:07.441118Z","iopub.execute_input":"2025-08-20T09:58:07.443007Z","iopub.status.idle":"2025-08-20T09:58:08.655471Z","shell.execute_reply.started":"2025-08-20T09:58:07.442987Z","shell.execute_reply":"2025-08-20T09:58:08.654818Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def make_dataset(x, y, batch_size=32, shuffle=False):\n    ds = tf.data.Dataset.from_tensor_slices((x, y))\n    if shuffle:\n        ds = ds.shuffle(1000)\n    ds = ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)\n    return ds\n\ntrain_ds = make_dataset(X_train_combined, y_train, shuffle=True)\nval_ds = make_dataset(X_val_combined, y_val)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:58:08.657273Z","iopub.execute_input":"2025-08-20T09:58:08.657469Z","iopub.status.idle":"2025-08-20T09:58:10.985672Z","shell.execute_reply.started":"2025-08-20T09:58:08.657454Z","shell.execute_reply":"2025-08-20T09:58:10.984905Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def make_model_a(input_shape=(28,28,2)):\n    model = tf.keras.Sequential([\n        tf.keras.layers.Conv2D(32, 3, activation='relu', input_shape=input_shape),\n        tf.keras.layers.MaxPooling2D(),\n        tf.keras.layers.Flatten(),\n        tf.keras.layers.Dense(64, activation='relu'),\n        tf.keras.layers.Dense(10, activation='softmax')\n    ])\n    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:58:10.986418Z","iopub.execute_input":"2025-08-20T09:58:10.986672Z","iopub.status.idle":"2025-08-20T09:58:10.991475Z","shell.execute_reply.started":"2025-08-20T09:58:10.986648Z","shell.execute_reply":"2025-08-20T09:58:10.990579Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def make_model_a(input_shape=(28,28,2)):\n    model = tf.keras.Sequential([\n        tf.keras.layers.Conv2D(32, 3, activation='relu', input_shape=input_shape),\n        tf.keras.layers.MaxPooling2D(),\n        tf.keras.layers.Flatten(),\n        tf.keras.layers.Dense(64, activation='relu'),\n        tf.keras.layers.Dense(10, activation='softmax')\n    ])\n    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:58:10.992319Z","iopub.execute_input":"2025-08-20T09:58:10.992573Z","iopub.status.idle":"2025-08-20T09:58:11.060528Z","shell.execute_reply.started":"2025-08-20T09:58:10.992551Z","shell.execute_reply":"2025-08-20T09:58:11.059635Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def make_model_b(input_shape=(28,28,2)):\n    inputs = tf.keras.Input(shape=input_shape)\n    x = tf.keras.layers.Conv2D(32, 3, activation='relu')(inputs)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.MaxPooling2D()(x)\n    x = tf.keras.layers.Conv2D(64, 3, activation='relu')(x)\n    x = tf.keras.layers.MaxPooling2D()(x)\n    x = tf.keras.layers.GlobalAveragePooling2D()(x)\n    x = tf.keras.layers.Dense(128, activation='relu')(x)\n    x = tf.keras.layers.Dropout(0.5)(x)\n    outputs = tf.keras.layers.Dense(10, activation='softmax')(x)\n    model = tf.keras.Model(inputs, outputs)\n    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:58:11.061429Z","iopub.execute_input":"2025-08-20T09:58:11.061670Z","iopub.status.idle":"2025-08-20T09:58:11.077889Z","shell.execute_reply.started":"2025-08-20T09:58:11.061651Z","shell.execute_reply":"2025-08-20T09:58:11.077064Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"datagen = tf.keras.preprocessing.image.ImageDataGenerator(\n    rotation_range=10,\n    zoom_range=0.1,\n    width_shift_range=0.1,\n    height_shift_range=0.1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:58:11.078691Z","iopub.execute_input":"2025-08-20T09:58:11.079010Z","iopub.status.idle":"2025-08-20T09:58:11.177099Z","shell.execute_reply.started":"2025-08-20T09:58:11.078988Z","shell.execute_reply":"2025-08-20T09:58:11.176411Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import keras_tuner as kt\n\ndef build_model(hp):\n    model = tf.keras.Sequential()\n    model.add(tf.keras.layers.Input(shape=(28,28,2)))\n    \n    for i in range(hp.Int(\"num_blocks\", 1, 3)):\n        model.add(tf.keras.layers.Conv2D(\n            filters=hp.Choice(f\"filters_{i}\", [32, 64, 128]),\n            kernel_size=3,\n            activation='relu'\n        ))\n        model.add(tf.keras.layers.MaxPooling2D())\n    \n    model.add(tf.keras.layers.GlobalAveragePooling2D())\n    model.add(tf.keras.layers.Dense(\n        hp.Choice(\"dense_units\", [64, 128, 256]),\n        activation='relu'\n    ))\n    model.add(tf.keras.layers.Dropout(hp.Float(\"dropout\", 0.0, 0.5, step=0.1)))\n    model.add(tf.keras.layers.Dense(10, activation='softmax'))\n    \n    model.compile(\n        optimizer=tf.keras.optimizers.Adam(hp.Float(\"lr\", 1e-4, 1e-2, sampling=\"log\")),\n        loss='sparse_categorical_crossentropy',\n        metrics=['accuracy']\n    )\n    return model\n\n# Search\ntuner = kt.RandomSearch(build_model, objective='val_accuracy', max_trials=10)\ntuner.search(train_ds, validation_data=val_ds, epochs=5)\n\n# Get best model\nbest_model = tuner.get_best_models(1)[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T09:58:11.177999Z","iopub.execute_input":"2025-08-20T09:58:11.178339Z","iopub.status.idle":"2025-08-20T10:02:08.060159Z","shell.execute_reply.started":"2025-08-20T09:58:11.178315Z","shell.execute_reply":"2025-08-20T10:02:08.059480Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_a = make_model_a()\nmodel_b = make_model_b()\n\nmodel_a.fit(train_ds, validation_data=val_ds, epochs=10, verbose=0)\nmodel_b.fit(train_ds, validation_data=val_ds, epochs=10, verbose=0)\n\n# Predict\npred_a = model_a.predict(X_val_combined)\npred_b = model_b.predict(X_val_combined)\npred_best = best_model.predict(X_val_combined)\n\n# Ensemble: average\nensemble_pred = (pred_a + pred_b + pred_best) / 3\nensemble_labels = np.argmax(ensemble_pred, axis=1)\n\n# Accuracy\nacc = np.mean(ensemble_labels == y_val)\nprint(f\"Ensemble Accuracy: {acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T10:02:08.061042Z","iopub.execute_input":"2025-08-20T10:02:08.061312Z","iopub.status.idle":"2025-08-20T10:03:20.191750Z","shell.execute_reply.started":"2025-08-20T10:02:08.061288Z","shell.execute_reply":"2025-08-20T10:03:20.191097Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Combine train + val\nX_full = np.concatenate([X_train, X_val], axis=0)\ny_full = np.concatenate([y_train, y_val], axis=0)\nX_full_combined = np.concatenate([X_train_combined, X_val_combined], axis=0)\n\n# Final train\nfinal_model = make_model_b()  # or best from tuner\nfull_ds = make_dataset(X_full_combined, y_full, shuffle=True)\nfinal_model.fit(full_ds, epochs=10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T10:03:20.192545Z","iopub.execute_input":"2025-08-20T10:03:20.192733Z","iopub.status.idle":"2025-08-20T10:04:00.958707Z","shell.execute_reply.started":"2025-08-20T10:03:20.192719Z","shell.execute_reply":"2025-08-20T10:04:00.958155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_preds = final_model.predict(X_test.reshape(-1, 28, 28, 2))  # if using edges\nconfidences = np.max(test_preds, axis=1)\nprint(\"Mean confidence:\", confidences.mean())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T10:04:00.959455Z","iopub.execute_input":"2025-08-20T10:04:00.959659Z","iopub.status.idle":"2025-08-20T10:04:02.745726Z","shell.execute_reply.started":"2025-08-20T10:04:00.959643Z","shell.execute_reply":"2025-08-20T10:04:02.744941Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_preds = final_model.predict(X_test.reshape(-1, 28, 28, 2))  # if using edges\nconfidences = np.max(test_preds, axis=1)\nprint(\"Mean confidence:\", confidences.mean())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T10:04:02.748054Z","iopub.execute_input":"2025-08-20T10:04:02.748490Z","iopub.status.idle":"2025-08-20T10:04:03.839435Z","shell.execute_reply.started":"2025-08-20T10:04:02.748471Z","shell.execute_reply":"2025-08-20T10:04:03.838815Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Final test predictions\nX_test_edges = add_edges_batch(X_test)\nX_test_combined = np.concatenate([X_test, X_test_edges], axis=-1)\nfinal_predictions = final_model.predict(X_test_combined)\nfinal_labels = np.argmax(final_predictions, axis=1)\n\n# Save\nsubmission = pd.DataFrame({\n    'ImageId': np.arange(1, len(final_labels) + 1),\n    'Label': final_labels\n})\nsubmission.to_csv('submission_final.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-20T10:04:03.840401Z","iopub.execute_input":"2025-08-20T10:04:03.840671Z","iopub.status.idle":"2025-08-20T10:04:07.022670Z","shell.execute_reply.started":"2025-08-20T10:04:03.840648Z","shell.execute_reply":"2025-08-20T10:04:07.022090Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}