{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":3649,"databundleVersionId":46718,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os, glob, numpy as np, pandas as pd\nfrom PIL import Image\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers, Model\nfrom tensorflow.keras.callbacks import Callback\n\n# ========= Repro & GPU info =========\nnp.random.seed(42)\ntf.random.set_seed(42)\nprint(\"TensorFlow:\", tf.__version__)\nprint(\"GPU:\", tf.config.list_physical_devices('GPU'))\n\nclass_names = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-18T05:28:21.886478Z","iopub.execute_input":"2025-10-18T05:28:21.887010Z","iopub.status.idle":"2025-10-18T05:28:22.670530Z","shell.execute_reply.started":"2025-10-18T05:28:21.886985Z","shell.execute_reply":"2025-10-18T05:28:22.669860Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================================\n# LOAD & PREPARE DATA — simple /255 normalization (no standard deviation)\n# ============================================================================\n(x_train, y_train), (x_val, y_val) = tf.keras.datasets.cifar10.load_data()\n\n# Normalize pixel values to [0,1]\nx_train = x_train.astype(\"float32\") / 255.0\nx_val   = x_val.astype(\"float32\") / 255.0\n\n# One-hot encode labels for categorical crossentropy\ny_train = tf.keras.utils.to_categorical(y_train, 10)\ny_val   = tf.keras.utils.to_categorical(y_val, 10)\n\nprint(\"x_train:\", x_train.shape, \"y_train:\", y_train.shape)\nprint(\"x_val:\", x_val.shape, \"y_val:\", y_val.shape)\n\n# # ============================================================================\n# # MIXUP PIPELINE (dtype-safe)\n# # ============================================================================\n# AUTO = tf.data.AUTOTUNE\n# BATCH = 128\n\n# def mixup_batch(x, y, alpha=0.2):\n#     x = tf.cast(x, tf.float32)\n#     y = tf.cast(y, tf.float32)\n\n#     lam = tf.random.gamma(shape=[1], alpha=alpha, dtype=x.dtype)\n#     lam = tf.squeeze(tf.minimum(lam, 1.0 - lam))\n#     idx = tf.random.shuffle(tf.range(tf.shape(x)[0]))\n\n#     x2 = tf.gather(x, idx)\n#     y2 = tf.gather(y, idx)\n\n#     x_m = lam * x + (1.0 - lam) * x2\n#     y_m = lam * y + (1.0 - lam) * y2\n#     return x_m, y_m\n\n# train_ds = (\n#     tf.data.Dataset.from_tensor_slices((x_train, y_train))\n#     .shuffle(50_000, seed=42, reshuffle_each_iteration=True)\n#     .batch(BATCH, drop_remainder=True)\n#     .map(lambda xb, yb: mixup_batch(xb, yb, alpha=0.2), num_parallel_calls=AUTO)\n#     .prefetch(AUTO)\n# )\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T05:28:26.362631Z","iopub.execute_input":"2025-10-18T05:28:26.363327Z","iopub.status.idle":"2025-10-18T05:28:31.658362Z","shell.execute_reply.started":"2025-10-18T05:28:26.363304Z","shell.execute_reply":"2025-10-18T05:28:31.657661Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================================\n# CIFAR-10 AUGMENTATION + MIXUP (robust & dtype-safe)\n# ============================================================================\n\nimport tensorflow as tf\nfrom tensorflow.keras import layers\n\nAUTO  = tf.data.AUTOTUNE\nBATCH = 128\n\n# ----------------------------\n# MixUp\n# ----------------------------\ndef mixup_batch(x, y, alpha=0.2):\n    \"\"\"Batch-wise MixUp (uniform-symmetric lambda; dtype-safe).\"\"\"\n    x = tf.cast(x, tf.float32)\n    y = tf.cast(y, tf.float32)\n\n    # Symmetric lambda in [0.5, 1.0] for balanced mixing\n    lam = tf.random.uniform([], 0.0, 1.0, dtype=x.dtype)\n    lam = tf.maximum(lam, 1.0 - lam)\n\n    idx = tf.random.shuffle(tf.range(tf.shape(x)[0]))\n    x2 = tf.gather(x, idx)\n    y2 = tf.gather(y, idx)\n\n    x_m = lam * x + (1.0 - lam) * x2\n    y_m = lam * y + (1.0 - lam) * y2\n    return x_m, y_m\n\n# ----------------------------\n# Advanced Augmentations (+Cutout via tf.pad)\n# ----------------------------\nclass AdvancedAugmentation(layers.Layer):\n    def __init__(self, mask_size=8, **kwargs):\n        super().__init__(**kwargs)\n        self.mask_size = mask_size\n        self.flip        = layers.RandomFlip(\"horizontal\")\n        self.rotation    = layers.RandomRotation(0.10)\n        self.zoom        = layers.RandomZoom(0.10)\n        self.translation = layers.RandomTranslation(0.10, 0.10)\n        self.contrast    = layers.RandomContrast(0.15)\n\n    def cutout_one(self, image):\n        \"\"\"\n        Create a (H,W,1) mask with a zeroed square hole and multiply.\n        Works with dynamic shapes and broadcasts over channels.\n        \"\"\"\n        image = tf.convert_to_tensor(image)\n        image = tf.cast(image, tf.float32)\n\n        H = tf.shape(image)[0]\n        W = tf.shape(image)[1]\n        # Use 1-channel mask and broadcast to 3\n        ms = tf.minimum(tf.minimum(self.mask_size, H), W)\n\n        # random top-left for the hole (inclusive end -> +1)\n        top  = tf.random.uniform([], 0, H - ms + 1, dtype=tf.int32)\n        left = tf.random.uniform([], 0, W - ms + 1, dtype=tf.int32)\n\n        # hole of zeros, pad with ones around\n        hole = tf.zeros([ms, ms, 1], dtype=image.dtype)\n        pad  = [\n            [top,  H - top  - ms],\n            [left, W - left - ms],\n            [0, 0]\n        ]\n        mask = tf.pad(hole, pad, constant_values=1.0)   # (H,W,1), ones with a zero hole\n        return image * mask                              # broadcast over channels\n\n    def call(self, images, training=None):\n        images = tf.cast(images, tf.float32)\n        if training:\n            images = self.flip(images)\n            images = self.rotation(images)\n            images = self.zoom(images)\n            images = self.translation(images)\n            images = self.contrast(images)\n\n            # 50% of batches get Cutout; map_fn keeps shapes intact\n            def apply_cutout():\n                return tf.map_fn(self.cutout_one, images, fn_output_signature=tf.float32)\n            images = tf.cond(tf.random.uniform([]) < 0.5, apply_cutout, lambda: images)\n        return images\n\n# ----------------------------\n# Datasets\n# ----------------------------\naugmenter = AdvancedAugmentation(mask_size=8)\n\ntrain_ds = (\n    tf.data.Dataset.from_tensor_slices((x_train, y_train))\n    .shuffle(50_000, seed=42, reshuffle_each_iteration=True)\n    .batch(BATCH, drop_remainder=True)\n    .map(lambda xb, yb: (augmenter(xb, training=True), yb), num_parallel_calls=AUTO)\n    .map(lambda xb, yb: mixup_batch(xb, yb, alpha=0.2), num_parallel_calls=AUTO)\n    .prefetch(AUTO)\n)\n\nval_ds = (\n    tf.data.Dataset.from_tensor_slices((x_val, y_val))\n    .batch(BATCH)\n    .prefetch(AUTO)\n)\n\nprint(\"✅ Augmentation + MixUp pipelines are ready.\")\n\n\n\n# class AdvancedAugmentation(layers.Layer):\n#     '''\n#     Applying various different augmentation techniques\n#     '''\n    \n#     def __init__(self, **kwargs):\n#         super().__init__(**kwargs)\n#         self.flip = layers.RandomFlip(\"horizontal\")\n#         self.rotation = layers.RandomRotation(0.10)\n#         self.zoom = layers.RandomZoom(0.10)\n#         self.translation = layers.RandomTranslation(0.10, 0.10)\n#         self.contrast = layers.RandomContrast(0.15)\n\n#     def cutout_one(self, image, mask_size=8):\n#         h = tf.random.uniform([], 0, 32 - mask_size, dtype=tf.int32)\n#         w = tf.random.uniform([], 0, 32 - mask_size, dtype=tf.int32)\n#         hole = tf.zeros([mask_size, mask_size, 3], dtype=image.dtype)\n#         mask = tf.pad(\n#             hole, [[h, 32 - h - mask_size], [w, 32 - w - mask_size], [0, 0]], constant_values=1.0\n#         )\n#         return image * mask\n\n#     def call(self, images, training=None):\n#         if training:\n#             images = self.flip(images)\n#             images = self.rotation(images)\n#             images = self.zoom(images)\n#             images = self.translation(images)\n#             images = self.contrast(images)\n#             def apply_cutout():\n#                 return tf.map_fn(self.cutout_one, images, dtype=images.dtype)\n#             images = tf.cond(tf.random.uniform([]) < 0.5, apply_cutout, lambda: images)\n#         return images\n\n# print(\"✅ AdvancedAugmentation ready\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T05:28:31.659431Z","iopub.execute_input":"2025-10-18T05:28:31.659719Z","iopub.status.idle":"2025-10-18T05:28:36.649979Z","shell.execute_reply.started":"2025-10-18T05:28:31.659699Z","shell.execute_reply":"2025-10-18T05:28:36.649275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def channel_attention(x, reduction=8):\n    c = x.shape[-1]\n    gap = layers.GlobalAveragePooling2D()(x)\n    gmp = layers.GlobalMaxPooling2D()(x)\n    d1 = layers.Dense(c // reduction, activation=\"relu\")\n    d2 = layers.Dense(c, activation=\"sigmoid\")\n    a1 = d2(d1(gap))\n    a2 = d2(d1(gmp))\n    attn = layers.Add()([a1, a2])\n    attn = layers.Reshape((1,1,c))(attn)\n    return layers.Multiply()([x, attn])\n\ndef spatial_attention(x):\n    avg = layers.Lambda(lambda t: tf.reduce_mean(t, axis=-1, keepdims=True))(x)\n    mx  = layers.Lambda(lambda t: tf.reduce_max(t, axis=-1, keepdims=True))(x)\n    concat = layers.Concatenate()([avg, mx])\n    attn = layers.Conv2D(1, 7, padding=\"same\", activation=\"sigmoid\")(concat)\n    return layers.Multiply()([x, attn])\n\nclass DropPath(layers.Layer):\n    def __init__(self, survival_prob=0.9):\n        super().__init__()\n        self.survival_prob = survival_prob\n    def call(self, x, training=None):\n        if (not training) or self.survival_prob == 1.0:\n            return x\n        keep = tf.random.uniform([tf.shape(x)[0],1,1,1]) < self.survival_prob\n        return tf.where(keep, x / self.survival_prob, tf.zeros_like(x))\n\ndef hybrid_residual_block(x, filters, stride=1, use_proj=False, use_attention=True, dp_survival=1.0):\n    shortcut = x\n    bneck = max(filters // 4, 16)\n\n    # 1x1\n    x = layers.Conv2D(bneck, 1, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x); x = layers.Activation(\"relu\")(x)\n    # 3x3\n    x = layers.Conv2D(bneck, 3, strides=stride, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x); x = layers.Activation(\"relu\")(x)\n    # 1x1\n    x = layers.Conv2D(filters, 1, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x)\n\n    if use_attention:\n        x = channel_attention(x)\n        x = spatial_attention(x)\n\n    if use_proj or stride != 1:\n        shortcut = layers.Conv2D(filters, 1, strides=stride, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(shortcut)\n        shortcut = layers.BatchNormalization()(shortcut)\n\n    x = DropPath(dp_survival)(x)\n    x = layers.Add()([x, shortcut])\n    x = layers.Activation(\"relu\")(x)\n    return x\n\ndef se_block(x, ratio=16):\n    channels = x.shape[-1]\n    se = layers.GlobalAveragePooling2D()(x)\n    se = layers.Dense(channels // ratio, activation='relu')(se)\n    se = layers.Dense(channels, activation='sigmoid')(se)\n    se = layers.Reshape((1, 1, channels))(se)\n    return layers.Multiply()([x, se])\n    \nprint(\"residual blocks and other model addition\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T05:28:37.792222Z","iopub.execute_input":"2025-10-18T05:28:37.792535Z","iopub.status.idle":"2025-10-18T05:28:37.806025Z","shell.execute_reply.started":"2025-10-18T05:28:37.792512Z","shell.execute_reply":"2025-10-18T05:28:37.805066Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_model(input_shape=(32, 32, 3), num_classes=10):\n    inputs = layers.Input(shape=input_shape)\n    x = AdvancedAugmentation()(inputs)\n\n    # ---- Stem ----\n    \n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n    x = layers.Conv2D(64, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n\n    # ---- Stage 1: two residual blocks, 32×32 ----\n    for _ in range(2):\n        shortcut = x\n        x = layers.Conv2D(64, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n        x = layers.BatchNormalization()(x)\n        x = layers.Activation(\"relu\")(x)\n        x = layers.Conv2D(64, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n        x = layers.BatchNormalization()(x)\n        x = layers.Add()([x, shortcut])\n        x = layers.Activation(\"relu\")(x)\n        x = se_block(x)\n\n    # ---- Stage 2: two blocks, 16×16 ----\n    # Downsample first\n    shortcut = layers.Conv2D(128, 1, strides=2, padding=\"same\", use_bias=False)(x)\n    shortcut = layers.BatchNormalization()(shortcut)\n    x = layers.Conv2D(128, 3, strides=2, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n    x = layers.Conv2D(128, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Add()([x, shortcut])\n    x = layers.Activation(\"relu\")(x)\n    x = se_block(x)\n\n    # One more residual block (same size)\n    shortcut = x\n    x = layers.Conv2D(128, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n    x = layers.Conv2D(128, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Add()([x, shortcut])\n    x = layers.Activation(\"relu\")(x)\n    x = se_block(x)\n\n    # ---- Stage 3: **Added block here → makes it \"ResNet-10\"**, 8×8 ----\n    shortcut = layers.Conv2D(256, 1, strides=2, padding=\"same\", use_bias=False)(x)\n    shortcut = layers.BatchNormalization()(shortcut)\n    x = layers.Conv2D(256, 3, strides=2, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n    x = layers.Conv2D(256, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Add()([x, shortcut])\n    x = layers.Activation(\"relu\")(x)\n    x = se_block(x)\n\n    # ✅ Extra residual block (the “+1” you’re adding)\n    for _ in range(2):\n        shortcut = x\n        x = layers.Conv2D(256, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n        x = layers.BatchNormalization()(x)\n        x = layers.Activation(\"relu\")(x)\n        x = layers.Conv2D(256, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n        x = layers.BatchNormalization()(x)\n        x = layers.Add()([x, shortcut])\n        x = layers.Activation(\"relu\")(x)\n        x = se_block(x)\n\n    # ---- Stage 4: 512 filters, 4×4 ----\n    shortcut = layers.Conv2D(512, 1, strides=2, padding=\"same\", use_bias=False)(x)\n    shortcut = layers.BatchNormalization()(shortcut)\n    x = layers.Conv2D(512, 3, strides=2, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n    x = layers.Conv2D(512, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Add()([x, shortcut])\n    x = layers.Activation(\"relu\")(x)\n    x = se_block(x)\n\n    # After the first Stage 4 block, add:\n    for _ in range(2):\n        shortcut = x\n        x = layers.Conv2D(512, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n        x = layers.BatchNormalization()(x)\n        x = layers.Activation(\"relu\")(x)\n        x = layers.Conv2D(512, 3, padding=\"same\", use_bias=False, kernel_initializer=\"he_normal\")(x)\n        x = layers.BatchNormalization()(x)\n        x = layers.Add()([x, shortcut])\n        x = layers.Activation(\"relu\")(x)\n        x = se_block(x)\n\n\n    # ---- Head ----\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dropout(0.4)(x)\n    outputs = layers.Dense(num_classes, activation=\"softmax\", kernel_initializer=\"he_normal\")(x)\n\n    model = Model(inputs, outputs, name=\"ResNet10_Simple\")\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T05:28:41.385144Z","iopub.execute_input":"2025-10-18T05:28:41.385785Z","iopub.status.idle":"2025-10-18T05:28:41.401327Z","shell.execute_reply.started":"2025-10-18T05:28:41.385757Z","shell.execute_reply":"2025-10-18T05:28:41.400430Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CosineAnnealingWarmupRestarts(tf.keras.callbacks.Callback):\n    def __init__(self, max_lr=1e-3, min_lr=1e-6, warmup_epochs=10, T_0=50, T_mult=2):\n        super().__init__()\n        self.max_lr = max_lr\n        self.min_lr = min_lr\n        self.warmup_epochs = warmup_epochs\n        self.T_0 = T_0\n        self.T_mult = T_mult\n        self.cycle_length = T_0\n        self.cycle = 0\n        self.epoch_in_cycle = 0\n    \n    def on_epoch_begin(self, epoch, logs=None):\n        if epoch < self.warmup_epochs:\n            # Linear warmup\n            lr = self.max_lr * (epoch + 1) / self.warmup_epochs\n        else:\n            # After warmup, use cosine with restarts\n            self.epoch_in_cycle = (epoch - self.warmup_epochs) % self.cycle_length\n            progress = self.epoch_in_cycle / self.cycle_length\n            lr = self.min_lr + (self.max_lr - self.min_lr) * 0.5 * (1 + np.cos(np.pi * progress))\n            \n            # Handle cycle restarts\n            if self.epoch_in_cycle + 1 == self.cycle_length:\n                self.cycle += 1\n                self.cycle_length = int(self.T_0 * (self.T_mult ** self.cycle))\n        \n        # Fix: Use assign method for newer Keras/TensorFlow\n        self.model.optimizer.learning_rate.assign(lr)\n        print(f\"Epoch {epoch+1}: LR = {lr:.6e}\")\n    \n    def on_epoch_end(self, epoch, logs=None):\n        logs = logs or {}\n        # Fix: Use numpy() to get the value\n        logs[\"lr\"] = float(self.model.optimizer.learning_rate.numpy())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T05:39:59.926035Z","iopub.execute_input":"2025-10-18T05:39:59.926322Z","iopub.status.idle":"2025-10-18T05:39:59.933582Z","shell.execute_reply.started":"2025-10-18T05:39:59.926303Z","shell.execute_reply":"2025-10-18T05:39:59.932739Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = build_model()   # <-- safe to call outside any strategy\nmodel.summary()\n\noptimizer = tf.keras.optimizers.AdamW(learning_rate=1e-3, weight_decay=1e-3)\n\n# labels are one-hot, so use CategoricalCrossentropy with smoothing\nloss = tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.05)\n\nmodel.compile(optimizer=optimizer, loss=loss, metrics=[\"accuracy\"])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T05:40:02.694045Z","iopub.execute_input":"2025-10-18T05:40:02.694321Z","iopub.status.idle":"2025-10-18T05:40:03.288195Z","shell.execute_reply.started":"2025-10-18T05:40:02.694297Z","shell.execute_reply":"2025-10-18T05:40:03.287644Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 300\ncosine = CosineAnnealingWarmupRestarts(\n    max_lr=1e-3,\n    min_lr=1e-6,\n    warmup_epochs=10,\n    T_0=50,\n    T_mult=2\n)\n\ncallbacks = [\n    cosine,\n    tf.keras.callbacks.EarlyStopping(monitor=\"val_accuracy\", patience=20, restore_best_weights=True, verbose=1),\n    tf.keras.callbacks.ModelCheckpoint(\"best_model.h5\", monitor=\"val_accuracy\", save_best_only=True, verbose=1),\n]\n\nhistory = model.fit(\n    x_train, y_train,\n    validation_data=(x_val, y_val),\n    epochs=EPOCHS,\n    batch_size=128,\n    callbacks=callbacks,\n    verbose=1,\n)\n\n# ============================================\n# ✅ Save best model weights for Kaggle\n# ============================================\n\n# Save the model checkpoint (weights only)\nmodel.save_weights(\"best_model.weights.h5\")\n\n\nprint(\"✅ Saved best_model.h5 — ready for Kaggle submission\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-18T05:40:08.178582Z","iopub.execute_input":"2025-10-18T05:40:08.179177Z","iopub.status.idle":"2025-10-18T05:41:04.233065Z","shell.execute_reply.started":"2025-10-18T05:40:08.179155Z","shell.execute_reply":"2025-10-18T05:41:04.231998Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ================================================================\n# ✅ Extract CIFAR-10 test images (from test.7z)\n# ================================================================\n!apt install -y libarchive-dev\n!pip install -q libarchive-c py7zr   # ✅ fixed typo: \"libarchiv\" → \"libarchive-c\"\n\nimport os, glob, py7zr\n\ntest_7z_path = \"/kaggle/input/cifar-10/test.7z\"   # adjust if dataset path differs\n\n# Verify file existence\nif not os.path.exists(test_7z_path):\n    raise FileNotFoundError(f\"⚠️ File not found: {test_7z_path}\")\n\n# Extraction directory\nextract_dir = \"./test\"\nos.makedirs(extract_dir, exist_ok=True)\n\nprint(\"📦 Extracting test.7z with py7zr...\")\n\nwith py7zr.SevenZipFile(test_7z_path, mode='r') as archive:\n    archive.extractall(path=extract_dir)\n\nprint(\"✅ Extraction completed.\")\n\n# Quick check\npng_files = glob.glob(os.path.join(extract_dir, \"*.png\"))\nprint(f\"🖼️ Found {len(png_files)} PNG test images in '{extract_dir}'\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, shutil, glob\n\nsrc = \"./test/test\"\ndst = \"./test\"\n\nif os.path.exists(src):\n    files = glob.glob(os.path.join(src, \"*.png\"))\n    print(f\"📦 Moving {len(files)} files from nested folder to top-level test/ ...\")\n\n    os.makedirs(dst, exist_ok=True)\n    moved = 0\n    for f in files:\n        shutil.move(f, dst)\n        moved += 1\n        if moved % 5000 == 0:\n            print(f\"  Moved {moved} files...\")\n\n    print(f\"✅ Done! Total moved: {moved}\")\n\n    # Try removing now-empty subfolder\n    try:\n        os.rmdir(src)\n        print(\"🧹 Removed empty ./test/test/ folder.\")\n    except Exception as e:\n        print(f\"(info) Could not remove ./test/test/: {e}\")\nelse:\n    print(\"No nested folder found.\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob\nprint(\"✅ Files now in ./test:\", len(glob.glob(\"./test/*.png\")))\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================================\n# FINAL KAGGLE SUBMISSION CELL — /255 normalization only (no mean/std)\n# ============================================================================\nimport os, glob, numpy as np, pandas as pd\nfrom PIL import Image\nimport tensorflow as tf\n\n# Load best weights (Keras 3 naming convention)\nif os.path.exists(\"best_model.h5\"):\n    model.load_weights(\"best_model.weights.h5\")\n    print(\"✅ Loaded best_model.weights.h5\")\nelse:\n    raise FileNotFoundError(\"❌ best_model.weights.h5 not found!\")\n\nmodel.trainable = False\n\n# ============================================================================\n# Load test images and preprocess (normalize only)\n# ============================================================================\ntest_dir = \"test\"\ntest_files = sorted(\n    glob.glob(os.path.join(test_dir, \"*.png\")),\n    key=lambda x: int(os.path.splitext(os.path.basename(x))[0])\n)\nprint(f\"✅ Found {len(test_files)} test images\")\n\ndef load_batch(paths):\n    \"\"\"Load and normalize test images\"\"\"\n    arr = np.zeros((len(paths), 32, 32, 3), dtype=\"float32\")\n    ids = np.zeros((len(paths),), dtype=\"int32\")\n    for i, p in enumerate(paths):\n        img = Image.open(p).convert(\"RGB\")\n        arr[i] = np.asarray(img, dtype=\"float32\") / 255.0   # ✅ Normalize only\n        ids[i] = int(os.path.splitext(os.path.basename(p))[0])\n    return arr, ids\n\n# ============================================================================\n# Predict in batches for efficiency\n# ============================================================================\nall_preds, all_ids = [], []\nB = 5000  # process 5000 images per batch\n\nfor s in range(0, len(test_files), B):\n    batch_files = test_files[s:s+B]\n    xb, ib = load_batch(batch_files)\n    pb = model.predict(xb, batch_size=256, verbose=1)\n    all_preds.append(pb)\n    all_ids.append(ib)\n\npreds = np.concatenate(all_preds, axis=0)\ntest_ids = np.concatenate(all_ids, axis=0)\npred_classes = np.argmax(preds, axis=1)\n\n# ============================================================================\n# Sanity check: prediction distribution\n# ============================================================================\nvals, cnts = np.unique(pred_classes, return_counts=True)\nclass_names = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']\n\nprint(\"\\n📊 Prediction class histogram:\")\nfor v, c in zip(vals, cnts):\n    print(f\"  {class_names[v]:12s}: {c:6d} ({c/len(pred_classes)*100:.1f}%)\")\n\nif len(vals) == 1:\n    print(\"\\n⚠️ WARNING: All predictions collapsed to ONE class!\")\nelif max(cnts) > len(pred_classes) * 0.4:\n    print(f\"\\n⚠️ WARNING: Class '{class_names[vals[np.argmax(cnts)]]}' \"\n          f\"has {max(cnts)/len(pred_classes)*100:.1f}% of predictions\")\n\n# ============================================================================\n# Create submission.csv\n# ============================================================================\n# ============================================================================\n# ✅ Create submission.csv with numeric labels\n# ============================================================================\nsubmission = pd.DataFrame({\n    \"id\": test_ids,\n    \"label\": [class_names[i] for i in pred_classes]\n}).sort_values(\"id\").reset_index(drop=True)\n\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"\\n✅ submission.csv created successfully!\")\nprint(submission.head(10))\nprint(f\"\\nTotal predictions: {len(submission)}\")\n\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}