{"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":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":3004,"databundleVersionId":861823,"isSourceIdPinned":false},{"sourceType":"kernelVersion","sourceId":306224812,"isSourceIdPinned":false}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport shutil\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.callbacks import (EarlyStopping,\n    LearningRateScheduler)\nfrom sklearn.model_selection import train_test_split\n\nnp.random.seed(42)\ntf.random.set_seed(42)\nos.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\"\n\n# ══════════════════════════════════════════════════════════════\n# SETTINGS — CHANGE ONLY THESE IF NEEDED\n# ══════════════════════════════════════════════════════════════\nWORK_DIR   = \"/kaggle/working\"\nDATA_PATH  = \"/kaggle/input/competitions/digit-recognizer\"\n# If you have a checkpoint dataset from previous session,\n# set this to its path. Otherwise leave as empty string \"\".\nCKPT_INPUT = \"/kaggle/input/notebooks/ankan2/notebook7f48f16496\"\n# Example: CKPT_INPUT = \"/kaggle/input/digit-ckpts\"\n\n# ══════════════════════════════════════════════════════════════\n# AUTO SAVE FUNCTION — saves after every single model\n# ══════════════════════════════════════════════════════════════\ndef auto_save_checkpoint(filename, data):\n    \"\"\"Save .npy file immediately after each model\"\"\"\n    path = os.path.join(WORK_DIR, filename)\n    np.save(path, data)\n    # Verify it saved correctly\n    if os.path.exists(path):\n        size = os.path.getsize(path)\n        print(f\"  SAVED: {filename} ({size/1024:.0f} KB)\")\n    else:\n        print(f\"  ERROR: Failed to save {filename}!\")\n    return path\n\ndef show_all_saved():\n    \"\"\"Show all saved checkpoints\"\"\"\n    files = sorted([f for f in os.listdir(WORK_DIR)\n                    if f.endswith(\".npy\")])\n    print(f\"\\nTotal saved checkpoints: {len(files)}\")\n    for f in files:\n        size = os.path.getsize(\n            os.path.join(WORK_DIR, f))\n        print(f\"  {f} ({size/1024:.0f} KB)\")\n    return files\n\ndef check_disk():\n    \"\"\"Check and clean disk space\"\"\"\n    _,_,free = shutil.disk_usage(WORK_DIR)\n    gb = free/1024/1024/1024\n    if gb < 2.0:\n        print(f\"  WARNING: Only {gb:.2f} GB free!\")\n        # Delete any large files\n        for f in os.listdir(WORK_DIR):\n            if f.endswith((\".keras\",\".h5\",\".pt\")):\n                path = os.path.join(WORK_DIR, f)\n                os.remove(path)\n                print(f\"  Deleted {f} to free space\")\n        _,_,free = shutil.disk_usage(WORK_DIR)\n        gb = free/1024/1024/1024\n    print(f\"  Disk free: {gb:.2f} GB\")\n    return gb\n\n# ══════════════════════════════════════════════════════════════\n# STEP 1 — CLEAN DISK\n# ══════════════════════════════════════════════════════════════\nprint(\"=\"*60)\nprint(\"STEP 1 — Cleaning disk\")\nprint(\"=\"*60)\nfor f in os.listdir(WORK_DIR):\n    if f.endswith((\".keras\",\".h5\")):\n        os.remove(os.path.join(WORK_DIR, f))\n        print(f\"Deleted: {f}\")\ncheck_disk()\n\n# ══════════════════════════════════════════════════════════════\n# STEP 2 — RESTORE FROM PREVIOUS SESSION\n# ══════════════════════════════════════════════════════════════\nprint(\"\\n\"+\"=\"*60)\nprint(\"STEP 2 — Restoring previous checkpoints\")\nprint(\"=\"*60)\nif CKPT_INPUT and os.path.exists(CKPT_INPUT):\n    restored = 0\n    for f in os.listdir(CKPT_INPUT):\n        if f.endswith(\".npy\"):\n            src = os.path.join(CKPT_INPUT, f)\n            dst = os.path.join(WORK_DIR, f)\n            if not os.path.exists(dst):\n                shutil.copy(src, dst)\n                print(f\"Restored: {f}\")\n                restored += 1\n    print(f\"Total restored: {restored} files\")\nelse:\n    print(\"No previous checkpoints — starting fresh!\")\n\nshow_all_saved()\n\n# ══════════════════════════════════════════════════════════════\n# STEP 3 — LOAD DATA\n# ══════════════════════════════════════════════════════════════\nprint(\"\\n\"+\"=\"*60)\nprint(\"STEP 3 — Loading data\")\nprint(\"=\"*60)\n\ntrain = pd.read_csv(f\"{DATA_PATH}/train.csv\")\ntest  = pd.read_csv(f\"{DATA_PATH}/test.csv\")\n\ny      = train[\"label\"]\nX      = train.drop(\"label\",axis=1).values.astype(\n         \"float32\") / 255.0\nX_test = test.values.astype(\"float32\") / 255.0\nX      = X.reshape(-1,28,28,1)\nX_test = X_test.reshape(-1,28,28,1)\ny      = to_categorical(y, num_classes=10)\n\nprint(\"Loading external MNIST...\")\n(X_mnist,y_mnist),_ = keras.datasets.mnist.load_data()\nX_mnist = X_mnist.astype(\"float32\") / 255.0\nX_mnist = X_mnist.reshape(-1,28,28,1)\ny_mnist = to_categorical(y_mnist, 10)\n\nX_train_full = np.concatenate([X, X_mnist])\ny_train_full = np.concatenate([y, y_mnist])\nprint(f\"Training samples:   {X_train_full.shape[0]}\")\n\nX_train,X_val,y_train,y_val = train_test_split(\n    X, y, test_size=0.1, random_state=42)\nprint(f\"Validation samples: {X_val.shape[0]}\")\n\n# ══════════════════════════════════════════════════════════════\n# STEP 4 — AUGMENTATION\n# ══════════════════════════════════════════════════════════════\ndef mixup_batch(X, y, alpha=0.2):\n    lam = np.random.beta(alpha, alpha)\n    idx = np.random.permutation(len(X))\n    Xm  = (lam*X + (1-lam)*X[idx]).astype(\"float32\")\n    ym  = (lam*y + (1-lam)*y[idx]).astype(\"float32\")\n    return Xm, ym\n\ndef cutout(X, n_holes=1, length=8):\n    X = X.copy()\n    for img in X:\n        for _ in range(n_holes):\n            yc = np.random.randint(28)\n            xc = np.random.randint(28)\n            y1 = max(0, yc-length//2)\n            y2 = min(28, yc+length//2)\n            x1 = max(0, xc-length//2)\n            x2 = min(28, xc+length//2)\n            img[y1:y2, x1:x2, :] = 0\n    return X\n\ndef augmented_generator(X, y, batch_size=64):\n    from tensorflow.keras.preprocessing.image import (\n        ImageDataGenerator)\n    datagen = ImageDataGenerator(\n        rotation_range=12,\n        zoom_range=0.12,\n        width_shift_range=0.12,\n        height_shift_range=0.12,\n        shear_range=0.1)\n    gen = datagen.flow(\n        X, y, batch_size=batch_size, shuffle=True)\n    while True:\n        Xb, yb = next(gen)\n        Xb     = cutout(Xb)\n        Xb, yb = mixup_batch(Xb, yb)\n        yield Xb, yb\n\ndef predict_tta(model, Xt, n_tta=25):\n    from tensorflow.keras.preprocessing.image import (\n        ImageDataGenerator)\n    tta = ImageDataGenerator(\n        rotation_range=8,\n        zoom_range=0.08,\n        width_shift_range=0.08,\n        height_shift_range=0.08)\n    preds = model.predict(Xt, batch_size=64, verbose=0)\n    for _ in range(n_tta-1):\n        aug    = next(tta.flow(\n            Xt, batch_size=len(Xt), shuffle=False))\n        preds += model.predict(\n            aug, batch_size=64, verbose=0)\n    return preds / n_tta\n\ndef cosine_schedule(epoch, lr):\n    return (1e-7 + 0.5*(0.001-1e-7) *\n            (1 + np.cos(np.pi*epoch/60)))\n\ndef get_callbacks():\n    return [\n        LearningRateScheduler(\n            cosine_schedule, verbose=0),\n        EarlyStopping(\n            monitor=\"val_accuracy\",\n            patience=12,\n            restore_best_weights=True,\n            verbose=1)\n    ]\n\n# ══════════════════════════════════════════════════════════════\n# CORE TRAINING FUNCTION — saves immediately after training\n# ══════════════════════════════════════════════════════════════\ndef train_one_model(model, X_tr, y_tr,\n                    batch_size, save_name):\n    \"\"\"\n    Train one model and save predictions immediately.\n    Returns predictions or None if failed.\n    \"\"\"\n    save_path = os.path.join(WORK_DIR, save_name)\n\n    # Already done — skip!\n    if os.path.exists(save_path):\n        print(f\"  Already done — loading {save_name}\")\n        return np.load(save_path)\n\n    try:\n        print(f\"  Training {save_name}...\")\n        steps = len(X_tr) // batch_size\n        gen   = augmented_generator(\n            X_tr, y_tr, batch_size)\n\n        model.fit(\n            gen,\n            steps_per_epoch=steps,\n            epochs=60,\n            validation_data=(X_val, y_val),\n            callbacks=get_callbacks(),\n            verbose=1\n        )\n\n        print(f\"  Generating TTA predictions...\")\n        preds = predict_tta(model, X_test, n_tta=25)\n\n        # SAVE IMMEDIATELY\n        auto_save_checkpoint(save_name, preds)\n        check_disk()\n\n        del model\n        tf.keras.backend.clear_session()\n        return preds\n\n    except Exception as e:\n        print(f\"  ERROR: {e}\")\n        print(f\"  Attempting retry with different seed...\")\n        try:\n            del model\n        except:\n            pass\n        tf.keras.backend.clear_session()\n        return None\n\n# ══════════════════════════════════════════════════════════════\n# STEP 5 — MODEL ARCHITECTURES\n# ══════════════════════════════════════════════════════════════\ndef build_cnn(seed=42):\n    tf.random.set_seed(seed)\n    inp = keras.Input(shape=(28,28,1))\n    x   = layers.Conv2D(32,(3,3),activation=\"relu\",\n                        padding=\"same\")(inp)\n    x   = layers.BatchNormalization()(x)\n    x   = layers.Conv2D(32,(3,3),activation=\"relu\",\n                        padding=\"same\")(x)\n    x   = layers.BatchNormalization()(x)\n    x   = layers.MaxPooling2D(2,2)(x)\n    x   = layers.Dropout(0.25)(x)\n    x   = layers.Conv2D(64,(3,3),activation=\"relu\",\n                        padding=\"same\")(x)\n    x   = layers.BatchNormalization()(x)\n    x   = layers.Conv2D(64,(3,3),activation=\"relu\",\n                        padding=\"same\")(x)\n    x   = layers.BatchNormalization()(x)\n    x   = layers.MaxPooling2D(2,2)(x)\n    x   = layers.Dropout(0.25)(x)\n    x   = layers.Conv2D(128,(3,3),activation=\"relu\",\n                        padding=\"same\")(x)\n    x   = layers.BatchNormalization()(x)\n    x   = layers.Conv2D(128,(3,3),activation=\"relu\",\n                        padding=\"same\")(x)\n    x   = layers.BatchNormalization()(x)\n    x   = layers.Dropout(0.25)(x)\n    x   = layers.Flatten()(x)\n    x   = layers.Dense(512,activation=\"relu\")(x)\n    x   = layers.BatchNormalization()(x)\n    x   = layers.Dropout(0.4)(x)\n    x   = layers.Dense(256,activation=\"relu\")(x)\n    x   = layers.BatchNormalization()(x)\n    x   = layers.Dropout(0.3)(x)\n    out = layers.Dense(10,activation=\"softmax\")(x)\n    m   = keras.Model(inp, out)\n    m.compile(\n        optimizer=keras.optimizers.Adam(0.001),\n        loss=keras.losses.CategoricalCrossentropy(\n            label_smoothing=0.1),\n        metrics=[\"accuracy\"])\n    return m\n\ndef build_effB0(seed=42):\n    tf.random.set_seed(seed)\n    inp  = keras.Input(shape=(28,28,1))\n    x    = layers.Resizing(32,32)(inp)\n    x    = layers.Conv2D(3,(1,1),padding=\"same\")(x)\n    base = keras.applications.EfficientNetB0(\n        include_top=False,weights=None,input_tensor=x)\n    out  = layers.GlobalAveragePooling2D()(base.output)\n    out  = layers.BatchNormalization()(out)\n    out  = layers.Dropout(0.4)(out)\n    out  = layers.Dense(256,activation=\"relu\")(out)\n    out  = layers.Dropout(0.3)(out)\n    out  = layers.Dense(10,activation=\"softmax\")(out)\n    m    = keras.Model(inp, out)\n    m.compile(\n        optimizer=keras.optimizers.Adam(0.001),\n        loss=keras.losses.CategoricalCrossentropy(\n            label_smoothing=0.1),\n        metrics=[\"accuracy\"])\n    return m\n\ndef build_effB3(seed=42):\n    tf.random.set_seed(seed)\n    inp  = keras.Input(shape=(28,28,1))\n    x    = layers.Resizing(48,48)(inp)\n    x    = layers.Conv2D(3,(1,1),padding=\"same\")(x)\n    base = keras.applications.EfficientNetB3(\n        include_top=False,weights=None,input_tensor=x)\n    out  = layers.GlobalAveragePooling2D()(base.output)\n    out  = layers.BatchNormalization()(out)\n    out  = layers.Dropout(0.4)(out)\n    out  = layers.Dense(256,activation=\"relu\")(out)\n    out  = layers.Dropout(0.3)(out)\n    out  = layers.Dense(10,activation=\"softmax\")(out)\n    m    = keras.Model(inp, out)\n    m.compile(\n        optimizer=keras.optimizers.Adam(0.001),\n        loss=keras.losses.CategoricalCrossentropy(\n            label_smoothing=0.1),\n        metrics=[\"accuracy\"])\n    return m\n\ndef build_effB7(seed=42):\n    tf.random.set_seed(seed)\n    inp  = keras.Input(shape=(28,28,1))\n    x    = layers.Resizing(96,96)(inp)\n    x    = layers.Conv2D(3,(1,1),padding=\"same\")(x)\n    base = keras.applications.EfficientNetB7(\n        include_top=False,weights=None,input_tensor=x)\n    out  = layers.GlobalAveragePooling2D()(base.output)\n    out  = layers.BatchNormalization()(out)\n    out  = layers.Dropout(0.5)(out)\n    out  = layers.Dense(512,activation=\"relu\")(out)\n    out  = layers.Dropout(0.4)(out)\n    out  = layers.Dense(10,activation=\"softmax\")(out)\n    m    = keras.Model(inp, out)\n    m.compile(\n        optimizer=keras.optimizers.Adam(0.001),\n        loss=keras.losses.CategoricalCrossentropy(\n            label_smoothing=0.1),\n        metrics=[\"accuracy\"])\n    return m\n\ndef build_densenet(seed=42):\n    tf.random.set_seed(seed)\n    inp  = keras.Input(shape=(28,28,1))\n    x    = layers.Resizing(32,32)(inp)\n    x    = layers.Conv2D(3,(1,1),padding=\"same\")(x)\n    base = keras.applications.DenseNet121(\n        include_top=False,weights=None,input_tensor=x)\n    out  = layers.GlobalAveragePooling2D()(base.output)\n    out  = layers.BatchNormalization()(out)\n    out  = layers.Dropout(0.4)(out)\n    out  = layers.Dense(256,activation=\"relu\")(out)\n    out  = layers.Dropout(0.3)(out)\n    out  = layers.Dense(10,activation=\"softmax\")(out)\n    m    = keras.Model(inp, out)\n    m.compile(\n        optimizer=keras.optimizers.Adam(0.001),\n        loss=keras.losses.CategoricalCrossentropy(\n            label_smoothing=0.1),\n        metrics=[\"accuracy\"])\n    return m\n\ndef build_resnet(seed=42):\n    tf.random.set_seed(seed)\n    inp  = keras.Input(shape=(28,28,1))\n    x    = layers.Resizing(56,56)(inp)\n    x    = layers.Conv2D(3,(1,1),padding=\"same\")(x)\n    base = keras.applications.ResNet50V2(\n        include_top=False,weights=None,input_tensor=x)\n    out  = layers.GlobalAveragePooling2D()(base.output)\n    out  = layers.BatchNormalization()(out)\n    out  = layers.Dropout(0.4)(out)\n    out  = layers.Dense(256,activation=\"relu\")(out)\n    out  = layers.Dropout(0.3)(out)\n    out  = layers.Dense(10,activation=\"softmax\")(out)\n    m    = keras.Model(inp, out)\n    m.compile(\n        optimizer=keras.optimizers.Adam(0.001),\n        loss=keras.losses.CategoricalCrossentropy(\n            label_smoothing=0.1),\n        metrics=[\"accuracy\"])\n    return m\n\nconfigs = [\n    (\"cnn\",   build_cnn,      8,  64),\n    (\"effB0\", build_effB0,    7,  64),\n    (\"effB3\", build_effB3,    5,  32),\n    (\"effB7\", build_effB7,    3,  16),\n    (\"dense\", build_densenet, 6,  32),\n    (\"resnet\",build_resnet,   6,  32),\n]\n\n# ══════════════════════════════════════════════════════════════\n# PHASE 1 — Train 35 models\n# ══════════════════════════════════════════════════════════════\nprint(\"\\n\"+\"=\"*60)\nprint(\"PHASE 1 — Training 35 models on 102k samples\")\nprint(\"=\"*60)\nall_preds = []\n\nfor name, builder, count, bsize in configs:\n    print(f\"\\n===== {name.upper()} ({count} models) =====\")\n    for i in range(count):\n        save_name = f\"p1_{name}_{i}.npy\"\n        model     = builder(seed=i*77)\n        preds     = train_one_model(\n            model, X_train_full, y_train_full,\n            bsize, save_name)\n\n        if preds is None:\n            print(f\"  Retry {name} {i+1}...\")\n            tf.keras.backend.clear_session()\n            model = builder(seed=i*77+999)\n            preds = train_one_model(\n                model, X_train_full, y_train_full,\n                bsize, save_name)\n\n        if preds is not None:\n            all_preds.append(preds)\n            print(f\"  {name} {i+1}/{count} done!\")\n\nshow_all_saved()\nprint(f\"\\nPhase 1 complete! Total: {len(all_preds)}\")\n\n# ══════════════════════════════════════════════════════════════\n# PHASE 2 — Pseudo Labels Round 1\n# ══════════════════════════════════════════════════════════════\nprint(\"\\n\"+\"=\"*60)\nprint(\"PHASE 2 — Pseudo Labeling Round 1\")\nprint(\"=\"*60)\np1_avg        = np.mean(all_preds, axis=0)\npseudo_labels = np.argmax(p1_avg, axis=1)\nconfidence    = np.max(p1_avg, axis=1)\nmask1         = confidence > 0.999\nX_ps1         = X_test[mask1]\ny_ps1         = to_categorical(pseudo_labels[mask1], 10)\nprint(f\"Pseudo samples: {mask1.sum()} / {len(X_test)}\")\nX_tr1 = np.concatenate([X_train_full, X_ps1])\ny_tr1 = np.concatenate([y_train_full, y_ps1])\nprint(f\"New training size: {X_tr1.shape[0]}\")\nauto_save_checkpoint(\"pseudo_mask1.npy\", mask1)\n\n# ══════════════════════════════════════════════════════════════\n# PHASE 3 — Retrain on pseudo round 1\n# ══════════════════════════════════════════════════════════════\nprint(\"\\n\"+\"=\"*60)\nprint(\"PHASE 3 — 35 models on pseudo round 1\")\nprint(\"=\"*60)\npseudo_preds1 = []\n\nfor name, builder, count, bsize in configs:\n    print(f\"\\n===== {name.upper()} pseudo1 =====\")\n    for i in range(count):\n        save_name = f\"p3_{name}_{i}.npy\"\n        model     = builder(seed=i*99+1)\n        preds     = train_one_model(\n            model, X_tr1, y_tr1,\n            bsize, save_name)\n\n        if preds is None:\n            print(f\"  Retry pseudo1 {name} {i+1}...\")\n            tf.keras.backend.clear_session()\n            model = builder(seed=i*99+888)\n            preds = train_one_model(\n                model, X_tr1, y_tr1,\n                bsize, save_name)\n\n        if preds is not None:\n            pseudo_preds1.append(preds)\n            print(f\"  {name} pseudo1 {i+1}/{count} done!\")\n\nshow_all_saved()\nprint(f\"\\nPhase 3 complete! Total: {len(pseudo_preds1)}\")\n\n# ══════════════════════════════════════════════════════════════\n# PHASE 4 — Pseudo Labels Round 2\n# ══════════════════════════════════════════════════════════════\nprint(\"\\n\"+\"=\"*60)\nprint(\"PHASE 4 — Pseudo Labeling Round 2\")\nprint(\"=\"*60)\ncombined       = all_preds + pseudo_preds1\np3_avg         = np.mean(combined, axis=0)\npseudo_labels2 = np.argmax(p3_avg, axis=1)\nconfidence2    = np.max(p3_avg, axis=1)\nmask2          = confidence2 > 0.995\nX_ps2          = X_test[mask2]\ny_ps2          = to_categorical(pseudo_labels2[mask2], 10)\nprint(f\"Round 2 samples: {mask2.sum()} / {len(X_test)}\")\nX_tr2 = np.concatenate([X_train_full, X_ps2])\ny_tr2 = np.concatenate([y_train_full, y_ps2])\nprint(f\"New training size: {X_tr2.shape[0]}\")\nauto_save_checkpoint(\"pseudo_mask2.npy\", mask2)\n\n# ══════════════════════════════════════════════════════════════\n# PHASE 5 — Retrain on pseudo round 2\n# ══════════════════════════════════════════════════════════════\nprint(\"\\n\"+\"=\"*60)\nprint(\"PHASE 5 — 35 models on pseudo round 2\")\nprint(\"=\"*60)\npseudo_preds2 = []\n\nfor name, builder, count, bsize in configs:\n    print(f\"\\n===== {name.upper()} pseudo2 =====\")\n    for i in range(count):\n        save_name = f\"p5_{name}_{i}.npy\"\n        model     = builder(seed=i*55+2)\n        preds     = train_one_model(\n            model, X_tr2, y_tr2,\n            bsize, save_name)\n\n        if preds is None:\n            print(f\"  Retry pseudo2 {name} {i+1}...\")\n            tf.keras.backend.clear_session()\n            model = builder(seed=i*55+777)\n            preds = train_one_model(\n                model, X_tr2, y_tr2,\n                bsize, save_name)\n\n        if preds is not None:\n            pseudo_preds2.append(preds)\n            print(f\"  {name} pseudo2 {i+1}/{count} done!\")\n\nshow_all_saved()\nprint(f\"\\nPhase 5 complete! Total: {len(pseudo_preds2)}\")\n\n# ══════════════════════════════════════════════════════════════\n# FINAL ENSEMBLE\n# ══════════════════════════════════════════════════════════════\nprint(\"\\n\"+\"=\"*60)\nprint(\"FINAL ENSEMBLE — 105 models\")\nprint(\"=\"*60)\nall_combined     = all_preds + pseudo_preds1 + pseudo_preds2\nfinal_preds      = np.mean(all_combined, axis=0)\npredicted_labels = np.argmax(final_preds, axis=1)\nprint(f\"Total models: {len(all_combined)}\")\n\nsubmission = pd.DataFrame({\n    \"ImageId\": range(1, len(predicted_labels)+1),\n    \"Label\":   predicted_labels\n})\nsubmission.to_csv(\n    f\"{WORK_DIR}/submission.csv\", index=False)\nauto_save_checkpoint(\n    \"final_predictions.npy\", final_preds)\n\nprint(\"\\n\"+\"=\"*60)\nprint(\"ALL DONE!\")\nprint(\"=\"*60)\nprint(submission.head(10))\ncheck_disk()\nprint(\"\\nSubmit submission.csv to Kaggle!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-25T05:40:00.714897Z","iopub.execute_input":"2026-03-25T05:40:00.715145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}