{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":16017,"databundleVersionId":708189}],"dockerImageVersionId":31328,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras import layers, models, callbacks\n\ntrain = pd.read_csv('/kaggle/input/competitions/Kannada-MNIST/train.csv')\ntest  = pd.read_csv('/kaggle/input/competitions/Kannada-MNIST/test.csv')\ndig   = pd.read_csv('/kaggle/input/competitions/Kannada-MNIST/Dig-MNIST.csv')\n\nprint(\"Train shape:\", train.shape)\nprint(\"Test shape :\", test.shape)\nprint(\"Dig-MNIST  :\", dig.shape)\nprint(\"Label distribution:\\n\", train['label'].value_counts().sort_index())\n\nfig, axes = plt.subplots(2, 5, figsize=(12, 5))\nfig.suptitle('Kannada Digits 0–9', fontsize=14)\nfor digit in range(10):\n    sample = train[train['label'] == digit].iloc[0, 1:].values.reshape(28, 28)\n    ax = axes[digit // 5][digit % 5]\n    ax.imshow(sample, cmap='gray')\n    ax.set_title(f'Digit {digit}')\n    ax.axis('off')\nplt.tight_layout()\nplt.show()\n\nX = train.drop('label', axis=1).values.reshape(-1, 28, 28, 1) / 255.0\ny = tf.keras.utils.to_categorical(train['label'], num_classes=10)\n\nX_test = test.drop('id', axis=1).values.reshape(-1, 28, 28, 1) / 255.0\nX_dig = dig.drop('label', axis=1).values.reshape(-1, 28, 28, 1) / 255.0\ny_dig = tf.keras.utils.to_categorical(dig['label'], num_classes=10)\n\nX_aug = np.concatenate([X, X_dig])\ny_aug = np.concatenate([y, y_dig])\n\nX_tr, X_val, y_tr, y_val = train_test_split(X_aug, y_aug, test_size=0.1, \n                                              random_state=42, stratify=y_aug.argmax(1))\nprint(f\"Training: {X_tr.shape}  |  Validation: {X_val.shape}\")\n\ndatagen = 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)\ndatagen.fit(X_tr)\n\ndef build_model():\n    inp = layers.Input(shape=(28, 28, 1))\n    x = layers.Conv2D(32, (3,3), padding='same', activation='relu')(inp)\n    x = layers.BatchNormalization()(x)\n    x = layers.Conv2D(32, (3,3), padding='same', activation='relu')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.MaxPooling2D()(x)\n    x = layers.Dropout(0.25)(x)\n\n    x = layers.Conv2D(64, (3,3), padding='same', activation='relu')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Conv2D(64, (3,3), padding='same', activation='relu')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.MaxPooling2D()(x)\n    x = layers.Dropout(0.25)(x)\n\n    x = layers.Flatten()(x)\n    x = layers.Dense(256, activation='relu')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.4)(x)\n    out = layers.Dense(10, activation='softmax')(x)\n\n    model = models.Model(inp, out)\n    return model\n\nmodel = build_model()\nmodel.summary()\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),\n    loss='categorical_crossentropy',\n    metrics=['accuracy']\n)\n\ncb_list = [\n    callbacks.EarlyStopping(patience=7, restore_best_weights=True, monitor='val_accuracy'),\n    callbacks.ReduceLROnPlateau(monitor='val_accuracy', patience=3, factor=0.5, verbose=1)\n]\n\nhistory = model.fit(\n    datagen.flow(X_tr, y_tr, batch_size=64),\n    epochs=50,\n    validation_data=(X_val, y_val),\n    callbacks=cb_list,\n    steps_per_epoch=len(X_tr) // 64\n)\n\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))\nax1.plot(history.history['accuracy'],     label='train acc')\nax1.plot(history.history['val_accuracy'], label='val acc')\nax1.set_title('Accuracy'); ax1.legend()\n\nax2.plot(history.history['loss'],     label='train loss')\nax2.plot(history.history['val_loss'], label='val loss')\nax2.set_title('Loss'); ax2.legend()\nplt.tight_layout(); plt.show()\n\ndig_preds = model.predict(X_dig).argmax(1)\ndig_true  = dig['label'].values\ndig_acc   = (dig_preds == dig_true).mean()\nprint(f\"Dig-MNIST out-of-domain accuracy: {dig_acc:.4f}\")\n\ntest_preds = model.predict(X_test).argmax(axis=1)\nsubmission = pd.DataFrame({'id': test['id'], 'label': test_preds})\nsubmission.to_csv('submission.csv', index=False)\nprint(submission.head(10))\nprint(\"Submission saved → submission.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-23T06:06:13.998375Z","iopub.execute_input":"2026-04-23T06:06:13.998611Z","iopub.status.idle":"2026-04-23T06:12:53.434785Z","shell.execute_reply.started":"2026-04-23T06:06:13.998588Z","shell.execute_reply":"2026-04-23T06:12:53.433938Z"}},"outputs":[],"execution_count":null}]}