{"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":3649,"databundleVersionId":46718,"sourceType":"competition"},{"sourceId":312126,"sourceType":"datasetVersion","datasetId":130727}],"dockerImageVersionId":31154,"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},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# DSEG660 Challenge — CIFAR-10 (from-scratch) Residual CNN\n# AdamW • Warmup→Cosine LR • CutMix + CutOut • Label Smoothing\n# ============================================================\n\nimport os, math, random\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\n\nfrom tensorflow.keras import layers as KL, models as KM, regularizers as KR\nfrom tensorflow.keras.datasets import cifar10\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\nfrom tensorflow.keras.optimizers import AdamW\n\nprint(\"TF:\", tf.__version__)\n\n# --------------------------\n# 0) Repro\n# --------------------------\nSEED = 1337\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntf.random.set_seed(SEED)\n\n# --------------------------\n# 1) Data (Keras CIFAR-10)  — used for train/val\n# --------------------------\ndef to_float_n1p1(x):\n    # map to [-1, 1]\n    x = x.astype(\"float32\") / 255.0\n    return (x - 0.5) * 2.0\n\n(x_tr_all, y_tr_all), (x_te_all, y_te_all) = cifar10.load_data()\nx_tr_all, x_te_all = to_float_n1p1(x_tr_all), to_float_n1p1(x_te_all)\ny_tr_all = y_tr_all.reshape(-1).astype(\"int32\")\ny_te_all = y_te_all.reshape(-1).astype(\"int32\")\n\nprint(\"Train:\", x_tr_all.shape, \"Test:\", x_te_all.shape)\n\n# hold out a small validation slice\nVAL_FRAC = 0.05\nn_all = x_tr_all.shape[0]\nn_val = int(VAL_FRAC * n_all)\nx_val, y_val = x_tr_all[:n_val], y_tr_all[:n_val]\nx_tr , y_tr  = x_tr_all[n_val:], y_tr_all[n_val:]\n\nIMG_H = IMG_W = 32\nN_CLS = 10\n\n# --------------------------\n# 2) Model — simple ResNet-ish stack (custom, no pretrain)\n# --------------------------\ndef conv_bn_relu(x, ch, s=1, wd=1e-4):\n    x = KL.Conv2D(ch, 3, strides=s, padding='same', use_bias=False,\n                  kernel_regularizer=KR.l2(wd))(x)\n    x = KL.BatchNormalization()(x)\n    return KL.Activation('relu')(x)\n\ndef res_block(inp, ch, down=False, wd=1e-4):\n    stride = 2 if down else 1\n    y = conv_bn_relu(inp, ch, s=stride, wd=wd)\n    y = conv_bn_relu(y, ch, s=1, wd=wd)\n    skip = inp\n    if down or inp.shape[-1] != ch:\n        skip = KL.Conv2D(ch, 1, strides=stride, padding='same', use_bias=False,\n                         kernel_regularizer=KR.l2(wd))(inp)\n        skip = KL.BatchNormalization()(skip)\n    out = KL.add([skip, y])\n    return KL.Activation('relu')(out)\n\ndef build_net():\n    inp = KL.Input(shape=(IMG_H, IMG_W, 3))\n    x = conv_bn_relu(inp, 64, s=1)\n\n    # Stage 1 (64): 3 blocks\n    for _ in range(3):\n        x = res_block(x, 64, down=False)\n\n    # Stage 2 (128): 3 blocks, first downsamples\n    x = res_block(x, 128, down=True)\n    x = res_block(x, 128, down=False)\n    x = res_block(x, 128, down=False)\n\n    # Stage 3 (256): 2 blocks, first downsamples\n    x = res_block(x, 256, down=True)\n    x = res_block(x, 256, down=False)\n\n    # Stage 4 (512): 2 blocks, first downsamples\n    x = res_block(x, 512, down=True)\n    x = res_block(x, 512, down=False)\n\n    # Head\n    x = KL.GlobalAveragePooling2D()(x)\n    x = KL.Dense(256, activation='relu')(x)\n    x = KL.Dropout(0.4)(x)\n    out = KL.Dense(N_CLS, activation='softmax')(x)\n    return KM.Model(inp, out, name=\"CustomResCNN\")\n\nmodel = build_net()\nmodel.summary()\n\n# --------------------------\n# 3) tf.data pipeline with CutMix + CutOut\n# --------------------------\nCUTMIX_P = 0.7\nCUTOUT_P = 0.5\nCUTOUT_SIZE = 8\n\n@tf.function\ndef _beta(alpha=1.0):\n    # reparameterization (same idea as Beta(a,a))\n    g1 = tf.random.gamma([], alpha, 1.0)\n    g2 = tf.random.gamma([], alpha, 1.0)\n    return g1 / (g1 + g2 + 1e-7)\n\ndef _cutmix(images, labels, alpha=1.0):\n    lam = _beta(alpha)\n    b = tf.shape(images)[0]\n    idx = tf.random.shuffle(tf.range(b))\n    img2 = tf.gather(images, idx)\n    lab2 = tf.gather(labels, idx)\n\n    H = IMG_H; W = IMG_W\n    cx = tf.random.uniform([], 0, W, dtype=tf.int32)\n    cy = tf.random.uniform([], 0, H, dtype=tf.int32)\n    cut_w = tf.cast(tf.sqrt(1. - lam) * tf.cast(W, tf.float32), tf.int32)\n\n    x1 = tf.clip_by_value(cx - cut_w//2, 0, W)\n    x2 = tf.clip_by_value(cx + cut_w//2, 0, W)\n    y1 = tf.clip_by_value(cy - cut_w//2, 0, H)\n    y2 = tf.clip_by_value(cy + cut_w//2, 0, H)\n\n    mask = tf.ones((y2 - y1, x2 - x1, 3), dtype=images.dtype)\n    mask = tf.image.pad_to_bounding_box(mask, y1, x1, H, W)\n    mixed = images * (1.0 - mask) + img2 * mask\n\n    lam_adj = 1. - tf.cast((x2-x1)*(y2-y1), tf.float32) / tf.cast(H*W, tf.float32)\n    y = lam_adj * tf.one_hot(labels, N_CLS) + (1. - lam_adj) * tf.one_hot(lab2, N_CLS)\n    return mixed, y\n\ndef _cutout(images, size=CUTOUT_SIZE):\n    def _one(img):\n        cx = tf.random.uniform([], 0, IMG_W, dtype=tf.int32)\n        cy = tf.random.uniform([], 0, IMG_H, dtype=tf.int32)\n        x1 = tf.clip_by_value(cx - size//2, 0, IMG_W)\n        x2 = tf.clip_by_value(cx + size//2, 0, IMG_W)\n        y1 = tf.clip_by_value(cy - size//2, 0, IMG_H)\n        y2 = tf.clip_by_value(cy + size//2, 0, IMG_H)\n        m = tf.ones((y2 - y1, x2 - x1, 3), dtype=img.dtype)\n        m = tf.image.pad_to_bounding_box(m, y1, x1, IMG_H, IMG_W)\n        return img * (1.0 - m)\n    return tf.map_fn(_one, images)\n\ndef _augment_batch(images, labels):\n    images = tf.image.random_flip_left_right(images)\n    images = tf.image.resize_with_crop_or_pad(images, IMG_H + 4, IMG_W + 4)\n    images = tf.image.random_crop(images, [tf.shape(images)[0], IMG_H, IMG_W, 3])\n\n    # CutMix OR identity → one-hot in the else branch\n    do_cm = tf.less(tf.random.uniform([]), CUTMIX_P)\n    images, labels = tf.cond(do_cm,\n                             lambda: _cutmix(images, labels, alpha=1.0),\n                             lambda: (images, tf.one_hot(labels, N_CLS)))\n\n    # CutOut after CutMix/no-CutMix\n    do_co = tf.less(tf.random.uniform([]), CUTOUT_P)\n    images = tf.cond(do_co, lambda: _cutout(images), lambda: images)\n    return images, labels\n\ndef make_dataset(x, y, train=True, batch=256, seed=SEED):\n    ds = tf.data.Dataset.from_tensor_slices((x, y))\n    if train:\n        ds = ds.shuffle(8192, seed=seed).batch(batch).map(_augment_batch, num_parallel_calls=tf.data.AUTOTUNE)\n    else:\n        ds = ds.batch(batch).map(lambda a, b: (a, tf.one_hot(b, N_CLS)), num_parallel_calls=tf.data.AUTOTUNE)\n    return ds.prefetch(tf.data.AUTOTUNE)\n\nBATCH = 256\nds_train = make_dataset(x_tr, y_tr, train=True,  batch=BATCH)\nds_val   = make_dataset(x_val, y_val, train=False, batch=BATCH)\n\n# --------------------------\n# 4) Optimizer & LR schedule (Warmup → Cosine), Label Smoothing\n# --------------------------\nEPOCHS = 240\nWARMUP_E = 5\nBASE_LR = 2e-3     # adjust with batch size if you change it\nMIN_LR  = 5e-6\nWEIGHT_DECAY = 1e-4\n\nsteps_per_epoch = math.ceil(x_tr.shape[0] / BATCH)\n\nclass WarmupCosine(tf.keras.optimizers.schedules.LearningRateSchedule):\n    def __init__(self, base_lr, warmup_epochs, total_epochs, steps_per_epoch, min_lr=0.0):\n        super().__init__()\n        self.base_lr = float(base_lr)\n        self.warm_steps = int(warmup_epochs * steps_per_epoch)\n        self.total_steps = int(total_epochs * steps_per_epoch)\n        self.min_lr = float(min_lr)\n\n    def __call__(self, step):\n        step = tf.cast(step, tf.float32)\n        warm = self.base_lr * (step / tf.maximum(1., tf.cast(self.warm_steps, tf.float32)))\n        prog = tf.clip_by_value((step - self.warm_steps) /\n                                tf.maximum(1., self.total_steps - self.warm_steps), 0., 1.)\n        cosv = self.min_lr + 0.5*(self.base_lr - self.min_lr)*(1. + tf.cos(math.pi * prog))\n        return tf.where(step < self.warm_steps, warm, cosv)\n\n    def get_config(self):\n        return dict(base_lr=self.base_lr, warm_steps=self.warm_steps,\n                    total_steps=self.total_steps, min_lr=self.min_lr)\n\nlr_sched = WarmupCosine(BASE_LR, WARMUP_E, EPOCHS, steps_per_epoch, MIN_LR)\nopt = AdamW(learning_rate=lr_sched, weight_decay=WEIGHT_DECAY)\n\n# Label smoothing via CCE(smoothing)\nmodel.compile(\n    optimizer=opt,\n    loss=tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.05),\n    metrics=[\"accuracy\"]\n)\n\n# --------------------------\n# 5) Train with early stop & best checkpoint\n# --------------------------\nckpt_cb = ModelCheckpoint(\"best.keras\", monitor=\"val_accuracy\", mode=\"max\",\n                          save_best_only=True, verbose=1)\nearly_cb = EarlyStopping(monitor=\"val_accuracy\", mode=\"max\",\n                         patience=30, restore_best_weights=True, verbose=1)\n\nhist = model.fit(\n    ds_train,\n    validation_data=ds_val,\n    epochs=EPOCHS,\n    callbacks=[ckpt_cb, early_cb],\n    verbose=2\n)\n\nval_loss, val_acc = model.evaluate(ds_val, verbose=0)\nprint(f\"✅ Holdout val acc: {val_acc:.4f} | val loss: {val_loss:.4f}\")\n\n# --------------------------\n# 6) Load best model for inference/export\n# --------------------------\nbest_model = tf.keras.models.load_model(\"best.keras\", compile=False)\nprint(\"✅ Loaded best checkpoint: best.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T05:16:51.815608Z","iopub.execute_input":"2025-10-09T05:16:51.815866Z","iopub.status.idle":"2025-10-09T06:51:13.942217Z","shell.execute_reply.started":"2025-10-09T05:16:51.815845Z","shell.execute_reply":"2025-10-09T06:51:13.941406Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Clean re-extract of OFFICIAL test set to /kaggle/working/cifar10_extracted/test\nfrom pathlib import Path\nimport shutil, subprocess, sys, os, glob\n\nCOMP_ROOT = Path(\"/kaggle/input/cifar-10\")\nWORK_ROOT = Path(\"/kaggle/working/cifar10_extracted\")\nTEST_DIR1 = WORK_ROOT / \"test\"          # usual\nTEST_DIR2 = WORK_ROOT / \"test\" / \"test\" # sometimes 7z nests one level\n\n# 0) Sanity: make sure the source exists\nassert (COMP_ROOT/\"test.7z\").exists(), \"Could not find /kaggle/input/cifar-10/test.7z\"\n\n# 1) Remove any partial test extraction (SAFE: only the 'test' subdir)\nfor d in [TEST_DIR2, TEST_DIR1]:\n    if d.exists():\n        print(\"Removing partial:\", d)\n        shutil.rmtree(d)\n\nWORK_ROOT.mkdir(parents=True, exist_ok=True)\n\n# 2) Re-extract test only (overwrite, multithread)\nprint(\"Extracting ONLY test.7z to\", WORK_ROOT)\nsubprocess.check_call([\"7z\",\"x\",\"-y\",\"-bb0\",\"-mmt=on\",\"-aoa\", f\"-o{str(WORK_ROOT)}\", str(COMP_ROOT/\"test.7z\")])\n\n# 3) Locate the real test folder (handles optional nested /test/test)\ndef find_test_dir(root: Path):\n    c1 = len(glob.glob(str(root/\"test/*.png\")))\n    c2 = len(glob.glob(str(root/\"test/test/*.png\")))\n    if c2 > 0 and c1 == 0:\n        return root/\"test/test\", c2\n    return root/\"test\", c1\n\nTEST_DIR, n = find_test_dir(WORK_ROOT)\nprint(\"Test dir:\", TEST_DIR)\nprint(\"PNG count:\", n)\nassert n == 300000, f\"Expected 300000 test PNGs, found {n}\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T13:30:59.765711Z","iopub.execute_input":"2025-10-09T13:30:59.765972Z","iopub.status.idle":"2025-10-09T17:39:20.924278Z","shell.execute_reply.started":"2025-10-09T13:30:59.765953Z","shell.execute_reply":"2025-10-09T17:39:20.923488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nTEST_DIR = Path(\"/kaggle/working/cifar10_extracted/test\")\nprint(\"official test pngs:\", len(list(TEST_DIR.glob(\"*.png\"))))  # should be 300000\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T17:50:02.610861Z","iopub.execute_input":"2025-10-09T17:50:02.611132Z","iopub.status.idle":"2025-10-09T17:50:03.518961Z","shell.execute_reply.started":"2025-10-09T17:50:02.611113Z","shell.execute_reply":"2025-10-09T17:50:03.518145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nDATASET_ROOT = Path(\"/kaggle/working/cifar10_extracted\")\ntest_paths = sorted((DATASET_ROOT/\"test\").glob(\"*.png\"), key=lambda p: int(p.stem))\nids = [int(p.stem) for p in test_paths]\nprint(len(ids), ids[:5], ids[-5:])   # should show 300000 and end at 300000\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T17:55:02.610111Z","iopub.execute_input":"2025-10-09T17:55:02.610400Z","iopub.status.idle":"2025-10-09T17:55:04.121760Z","shell.execute_reply.started":"2025-10-09T17:55:02.610379Z","shell.execute_reply":"2025-10-09T17:55:04.120982Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\n\ndef load_and_preprocess(path):\n    img = tf.io.read_file(path)              # path is a tf.string\n    img = tf.io.decode_png(img, 3)\n    img = tf.image.resize(img, [32, 32])\n    img = tf.image.convert_image_dtype(img, tf.float32)\n    return (img - 0.5) * 2.0\n\nBATCH = 1024\n\n# Convert Path objects -> strings\nimg_paths = [str(p) for p in test_paths]\n\ntest_ds = (tf.data.Dataset.from_tensor_slices(img_paths)\n           .map(load_and_preprocess, num_parallel_calls=tf.data.AUTOTUNE)\n           .batch(BATCH)\n           .prefetch(tf.data.AUTOTUNE))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T18:07:00.579361Z","iopub.execute_input":"2025-10-09T18:07:00.579940Z","iopub.status.idle":"2025-10-09T18:07:01.081736Z","shell.execute_reply.started":"2025-10-09T18:07:00.579918Z","shell.execute_reply":"2025-10-09T18:07:01.081092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Option A: peek one batch\nfor x in test_ds.take(1):\n    print(x.shape, x.dtype)   # e.g. (1024, 32, 32, 3) tf.float32\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T18:08:45.875178Z","iopub.execute_input":"2025-10-09T18:08:45.875911Z","iopub.status.idle":"2025-10-09T18:08:46.014332Z","shell.execute_reply.started":"2025-10-09T18:08:45.875888Z","shell.execute_reply":"2025-10-09T18:08:46.013689Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === QUICK REBUILD OF best.keras (short training just to create the file) ===\nimport tensorflow as tf, math\nfrom tensorflow.keras import layers as KL, models as KM\nfrom tensorflow.keras.callbacks import ModelCheckpoint\nfrom tensorflow.keras.optimizers import AdamW\n\n# 1) Load CIFAR-10 and normalize to [-1,1]\n(x_train, y_train), _ = tf.keras.datasets.cifar10.load_data()\nx_train = (x_train.astype(\"float32\")/255.0 - 0.5) * 2.0\ny_train = y_train.reshape(-1).astype(\"int32\")\n\n# small val split\nn = x_train.shape[0]; n_val = int(0.05*n)\nx_val, y_val = x_train[:n_val], y_train[:n_val]\nx_tr , y_tr  = x_train[n_val:], y_train[n_val:]\n\n# 2) Build a model\ntry:\n    # If your custom builder exists, use it for best results\n    model = build_custom_resnet()\nexcept NameError:\n    # Fallback simple CNN (only used if your builder isn't defined)\n    inputs = KL.Input((32,32,3))\n    x = KL.Conv2D(64,3,padding=\"same\",activation=\"relu\")(inputs)\n    x = KL.Conv2D(64,3,padding=\"same\",activation=\"relu\")(x)\n    x = KL.MaxPool2D()(x)\n    x = KL.Conv2D(128,3,padding=\"same\",activation=\"relu\")(x)\n    x = KL.Conv2D(128,3,padding=\"same\",activation=\"relu\")(x)\n    x = KL.GlobalAveragePooling2D()(x)\n    x = KL.Dense(256,activation=\"relu\")(x)\n    outputs = KL.Dense(10,activation=\"softmax\")(x)\n    model = KM.Model(inputs, outputs)\n\n# 3) Compile (simple, fast)\nmodel.compile(\n    optimizer=AdamW(learning_rate=2e-3),\n    loss=tf.keras.losses.SparseCategoricalCrossentropy(),\n    metrics=[\"accuracy\"]\n)\n\n# 4) Short train to produce best.keras\nckpt = ModelCheckpoint(\"best.keras\", monitor=\"val_accuracy\", mode=\"max\",\n                       save_best_only=True, verbose=1)\nmodel.fit(\n    x_tr, y_tr,\n    validation_data=(x_val, y_val),\n    epochs=3,            # keep small just to create the file quickly\n    batch_size=256,\n    callbacks=[ckpt],\n    verbose=2\n)\n\nprint(\"✅ Wrote best.keras to /kaggle/working\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T18:25:09.463780Z","iopub.execute_input":"2025-10-09T18:25:09.464517Z","iopub.status.idle":"2025-10-09T18:25:47.083508Z","shell.execute_reply.started":"2025-10-09T18:25:09.464492Z","shell.execute_reply":"2025-10-09T18:25:47.082897Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# RIGHT AFTER the quick-train cell, still before Step 3:\nimport tensorflow as tf\nmodel = tf.keras.models.load_model(\"/kaggle/working/best.keras\", compile=False)\nprint(\"Loaded /kaggle/working/best.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T18:26:32.526936Z","iopub.execute_input":"2025-10-09T18:26:32.527572Z","iopub.status.idle":"2025-10-09T18:26:32.600144Z","shell.execute_reply.started":"2025-10-09T18:26:32.527543Z","shell.execute_reply":"2025-10-09T18:26:32.599497Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Step 3: inference on OFFICIAL test set -> submission.csv ===\nimport math, numpy as np, pandas as pd, tensorflow as tf\n\n# Use the in-memory model if present; otherwise load the checkpoint we just wrote\nif 'model' not in globals() or not isinstance(model, tf.keras.Model):\n    model = tf.keras.models.load_model(\"/kaggle/working/best.keras\", compile=False)\nprint(\"Using model for inference.\")\n\n@tf.function\ndef fwd(x):\n    return model(x, training=False)\n\npred_chunks = []\nfor i, batch in enumerate(test_ds, 1):\n    logits = fwd(batch)\n    pred_chunks.append(tf.argmax(logits, axis=1))\n    if i % 25 == 0:\n        print(f\"processed {i}/{math.ceil(len(ids)/BATCH)}\")\n\npred_idx = tf.concat(pred_chunks, axis=0).numpy()\n\nCLASS_NAMES = [\"airplane\",\"automobile\",\"bird\",\"cat\",\"deer\",\"dog\",\"frog\",\"horse\",\"ship\",\"truck\"]\nsubmission = pd.DataFrame(\n    {\"id\": ids, \"label\": [CLASS_NAMES[i] for i in pred_idx]}\n)\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"✅ Saved submission.csv\")\ndisplay(submission.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T18:27:02.684688Z","iopub.execute_input":"2025-10-09T18:27:02.685167Z","iopub.status.idle":"2025-10-09T18:27:23.098968Z","shell.execute_reply.started":"2025-10-09T18:27:02.685138Z","shell.execute_reply":"2025-10-09T18:27:23.098179Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# CIFAR-10 — Build submission.csv from best.keras (paraphrased)\n# ============================================================\n\nimport os\nfrom pathlib import Path\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\n\n# ---------- Config: dataset mount that has sampleSubmission.csv ----------\nDATA_ROOT = Path(\"/kaggle/input/cifar10-object-recognition-in-images-zip-file\")\nSAMPLE_SUB = DATA_ROOT / \"sampleSubmission.csv\"\n\n# Test images in this dataset can live under either:\n#   .../train_test/test/*.png       or       .../train_test/test/test/*.png\nTEST_A = DATA_ROOT / \"train_test\" / \"test\"\nTEST_B = DATA_ROOT / \"train_test\" / \"test\" / \"test\"\nTEST_DIR = TEST_B if TEST_B.exists() else TEST_A\n\nprint(\"DATA_ROOT:\", DATA_ROOT)\nprint(\"TEST_DIR :\", TEST_DIR)\n\n# ---------- Load the trained model ----------\nmodel = tf.keras.models.load_model(\"best.keras\", compile=False)\nprint(\"✅ Loaded checkpoint: best.keras\")\n\n# ---------- Read the expected IDs from the sample file ----------\nsample_df = pd.read_csv(SAMPLE_SUB)\nids = sample_df[\"id\"].astype(str).tolist()\nprint(f\"🧾 IDs to predict: {len(ids)}\")\n\n# ---------- Resolve a file path for every id ----------\ndef path_for_id(img_id: str) -> str:\n    # prefer .png, fall back to .jpg\n    p_png = TEST_DIR / f\"{img_id}.png\"\n    if p_png.exists():\n        return str(p_png)\n    p_jpg = TEST_DIR / f\"{img_id}.jpg\"\n    if p_jpg.exists():\n        return str(p_jpg)\n    raise FileNotFoundError(f\"Missing image for id={img_id} in {TEST_DIR}\")\n\nimg_paths = [path_for_id(i) for i in ids]\n\n# ---------- tf.data loader: decode → float32 → resize 32x32 → [-1,1] ----------\ndef load_and_preprocess(path):\n    img = tf.io.read_file(path)\n    img = tf.io.decode_image(img, channels=3, expand_animations=False)\n    img = tf.image.resize(img, [32, 32])\n    img = tf.image.convert_image_dtype(img, tf.float32)  # [0,1]\n    return (img - 0.5) * 2.0                              # [-1,1]\n\nBATCH = 1024\ntest_ds = (\n    tf.data.Dataset.from_tensor_slices(img_paths)\n    .map(load_and_preprocess, num_parallel_calls=tf.data.AUTOTUNE)\n    .batch(BATCH)\n    .prefetch(tf.data.AUTOTUNE)\n)\n\n# ---------- Predict and write submission ----------\nprob_chunks = model.predict(test_ds, verbose=1)\npred_idx = np.argmax(prob_chunks, axis=1)\n\nCLASS_NAMES = [\"airplane\",\"automobile\",\"bird\",\"cat\",\"deer\",\"dog\",\"frog\",\"horse\",\"ship\",\"truck\"]\npred_labels = [CLASS_NAMES[i] for i in pred_idx]\n\nsubmission = pd.DataFrame({\"id\": ids, \"label\": pred_labels})\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"📁 Saved submission.csv\")\ndisplay(submission.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T06:57:28.787716Z","iopub.execute_input":"2025-10-09T06:57:28.788239Z","execution_failed":"2025-10-09T11:37:58.696Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import math, numpy as np, pandas as pd, tensorflow as tf\n\nmodel = tf.keras.models.load_model(\"best.keras\", compile=False)\n\n@tf.function\ndef fwd(x): return model(x, training=False)\n\npred_idx = []\ni = 0\nfor batch in test_ds:             # reuse the same test_ds\n    logits = fwd(batch)\n    pred_idx.append(tf.argmax(logits, axis=1))\n    i += 1\n    if i % 25 == 0:\n        print(f\"processed {i}/{math.ceil(len(ids)/BATCH)} batches\")\n\npred_idx = tf.concat(pred_idx, axis=0).numpy()\n\nCLASS_NAMES = [\"airplane\",\"automobile\",\"bird\",\"cat\",\"deer\",\"dog\",\"frog\",\"horse\",\"ship\",\"truck\"]\nsubmission = pd.DataFrame({\"id\": ids, \"label\": [CLASS_NAMES[i] for i in pred_idx]})\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"✅ Saved submission.csv\")\ndisplay(submission.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T07:37:13.154435Z","iopub.execute_input":"2025-10-09T07:37:13.154713Z","iopub.status.idle":"2025-10-09T07:39:46.508102Z","shell.execute_reply.started":"2025-10-09T07:37:13.154691Z","shell.execute_reply":"2025-10-09T07:39:46.507437Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==== CIFAR-10 | Submission QA (paraphrased) ====\nimport pandas as pd\nimport numpy as np\n\nCSV_PATH = \"submission.csv\"\nCLASSES = {\"airplane\",\"automobile\",\"bird\",\"cat\",\"deer\",\"dog\",\"frog\",\"horse\",\"ship\",\"truck\"}\n\ndf = pd.read_csv(CSV_PATH)\n\n# 1) Basic structure\nprint(f\"Shape: {df.shape}\")                     # expect (300000, 2)\nprint(\"Columns:\", list(df.columns))             # expect ['id', 'label']\n\n# 2) ID integrity\nids = df[\"id\"]\nprint(\"IDs unique:\", ids.is_unique)             # expect True\nprint(\"ID dtype:\", ids.dtype)\nprint(\"ID min/max:\", ids.min(), \"→\", ids.max())\n\n# Optional: are IDs exactly 1..300000?\nexpected_n = 300000\nif len(df) == expected_n:\n    # Only do this set check if size matches (avoids huge memory for mismatched sizes)\n    exact_range_ok = set(ids.values) == set(range(1, expected_n+1))\n    print(\"IDs cover 1..300000 exactly:\", exact_range_ok)\n\n# 3) Nulls\nprint(\"Nulls by column:\", df.isna().sum().to_dict())\n\n# 4) Labels sanity\nlbls = df[\"label\"]\nunknown = set(lbls.unique()) - CLASSES\nprint(\"Only known class names:\", len(unknown) == 0)\nif unknown:\n    print(\"Unknown labels found:\", unknown)\n\n# 5) Distribution snapshot\nprint(\"\\nLabel distribution:\")\nprint(lbls.value_counts().sort_index())\n\n# 6) Peek at random rows (reproducible)\nSAMPLE_SEED = 1337\nprint(\"\\nRandom sample:\")\nprint(df.sample(10, random_state=SAMPLE_SEED))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T07:48:14.429374Z","iopub.execute_input":"2025-10-09T07:48:14.429904Z","iopub.status.idle":"2025-10-09T07:48:14.626954Z","shell.execute_reply.started":"2025-10-09T07:48:14.429878Z","shell.execute_reply":"2025-10-09T07:48:14.626225Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}