{"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,"isSourceIdPinned":false,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nprint(os.listdir(\"/kaggle/input\"))\nprint(os.listdir(\"/kaggle/input/cifar-10\"))\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-19T12:48:44.802921Z","iopub.execute_input":"2025-09-19T12:48:44.803536Z","iopub.status.idle":"2025-09-19T12:48:44.811483Z","shell.execute_reply.started":"2025-09-19T12:48:44.803511Z","shell.execute_reply":"2025-09-19T12:48:44.810805Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Extract Kaggle competition test set into ./test/\n!apt -yqq install libarchive-dev >/dev/null\n!pip -q install libarchive >/dev/null\n\nimport os, glob, libarchive.public\n\nseven_z = \"/kaggle/input/cifar-10/test.7z\"\nassert os.path.exists(seven_z), \"Add the 'cifar-10' competition as an Input first.\"\n\nif not os.path.exists(\"test\"):\n    os.makedirs(\"test\", exist_ok=True)\n    cnt = 0\n    for _ in libarchive.public.file_pour(seven_z):\n        cnt += 1\n        if cnt % 10000 == 0:\n            print(\"Extracted:\", cnt)\n    print(\"Extraction finished.\")\nelse:\n    print(\"Folder 'test' already exists; skipping.\")\n\n# Verify count ~300,000 files\nprint(\"Files in test/:\", len(glob.glob(\"test/*\")))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-19T15:46:48.365634Z","iopub.execute_input":"2025-09-19T15:46:48.365866Z","iopub.status.idle":"2025-09-19T15:48:15.850916Z","shell.execute_reply.started":"2025-09-19T15:46:48.365847Z","shell.execute_reply":"2025-09-19T15:48:15.849913Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 2 — load official CIFAR-10 and make a 5k validation split\nimport numpy as np\nfrom tensorflow.keras.datasets import cifar10\n\n# 50k train / 10k test from the official dataset\n(x_train_full, y_train_full), (x_test, y_test) = cifar10.load_data()\n\n# 5,000 images for validation\nVAL = 5000\nx_val   = x_train_full[-VAL:]\ny_val   = y_train_full[-VAL:]\nx_train = x_train_full[:-VAL]\ny_train = y_train_full[:-VAL]\n\n# Flatten labels to shape (N,)\ny_train = y_train.squeeze().astype(np.int32)\ny_val   = y_val.squeeze().astype(np.int32)\ny_test  = y_test.squeeze().astype(np.int32)\n\nCLASS_NAMES = ['airplane','automobile','bird','cat','deer',\n               'dog','frog','horse','ship','truck']\n\nprint(\"Train:\", x_train.shape, y_train.shape)\nprint(\"Val:  \", x_val.shape,   y_val.shape)\nprint(\"Test: \", x_test.shape,  y_test.shape)\nprint(\"Classes:\", CLASS_NAMES)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T15:31:23.395829Z","iopub.execute_input":"2025-09-20T15:31:23.396585Z","iopub.status.idle":"2025-09-20T15:31:36.882592Z","shell.execute_reply.started":"2025-09-20T15:31:23.396558Z","shell.execute_reply":"2025-09-20T15:31:36.881973Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 3 — tf.data pipeline (NO rotations)\nimport tensorflow as tf\n\nAUTO       = tf.data.AUTOTUNE\nIMG_SIZE   = 32\nBATCH_SIZE = 128\nSEED       = 42\n\ndef augment(img, label):\n    # to float in [0,1]\n    img = tf.cast(img, tf.float32) / 255.0\n\n    # classic CIFAR trick: pad + random crop\n    img = tf.pad(img, [[4,4],[4,4],[0,0]], mode=\"REFLECT\")\n    img = tf.image.random_crop(img, [IMG_SIZE, IMG_SIZE, 3], seed=SEED)\n\n    # horizontal flip only (NO rotations)\n    img = tf.image.random_flip_left_right(img, seed=SEED)\n\n    # mild color jitter\n    img = tf.image.random_brightness(img, max_delta=0.1)\n    img = tf.image.random_contrast(img, lower=0.9, upper=1.1)\n\n    # small cutout (random erase)\n    size = tf.random.uniform([], 6, 12, dtype=tf.int32, seed=SEED)\n    cx   = tf.random.uniform([], 0, IMG_SIZE, dtype=tf.int32, seed=SEED)\n    cy   = tf.random.uniform([], 0, IMG_SIZE, dtype=tf.int32, seed=SEED)\n    y1 = tf.clip_by_value(cy - size // 2, 0, IMG_SIZE)\n    x1 = tf.clip_by_value(cx - size // 2, 0, IMG_SIZE)\n    y2 = tf.clip_by_value(y1 + size, 0, IMG_SIZE)\n    x2 = tf.clip_by_value(x1 + size, 0, IMG_SIZE)\n    mask = tf.ones([y2 - y1, x2 - x1, 3], tf.float32)\n    pad  = tf.pad(mask, [[y1, IMG_SIZE - y2], [x1, IMG_SIZE - x2], [0,0]])\n    img  = img * (1. - pad)\n\n    return img, label\n\ndef normalize(img, label):\n    return tf.cast(img, tf.float32) / 255.0, label\n\ndef make_ds(images, labels, training):\n    ds = tf.data.Dataset.from_tensor_slices((images, labels))\n    if training:\n        ds = ds.shuffle(10_000, seed=SEED, reshuffle_each_iteration=True)\n        ds = ds.map(augment, num_parallel_calls=AUTO)\n    else:\n        ds = ds.map(normalize, num_parallel_calls=AUTO)\n    ds = ds.batch(BATCH_SIZE).prefetch(AUTO)\n    return ds\n\ntrain_ds = make_ds(x_train, y_train, training=True)\nval_ds   = make_ds(x_val,   y_val,   training=False)\ntest_ds  = make_ds(x_test,  y_test,  training=False)\n\nprint(\"Pipelines ready (NO rotations). One training batch:\",\n      next(iter(train_ds.take(1)))[0].shape)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T15:39:05.044988Z","iopub.execute_input":"2025-09-20T15:39:05.045741Z","iopub.status.idle":"2025-09-20T15:39:06.191447Z","shell.execute_reply.started":"2025-09-20T15:39:05.045716Z","shell.execute_reply":"2025-09-20T15:39:06.190806Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 4 — ResNet-18 for CIFAR-10 (random init)\nfrom tensorflow import keras\nfrom tensorflow.keras import layers, regularizers\n\nWEIGHT_DECAY = 5e-4  # standard for CIFAR training\n\ndef conv3x3(filters, stride=1):\n    return layers.Conv2D(\n        filters, 3, strides=stride, padding=\"same\", use_bias=False,\n        kernel_initializer=\"he_normal\",\n        kernel_regularizer=regularizers.l2(WEIGHT_DECAY)\n    )\n\ndef basic_block(x, filters, stride):\n    y = conv3x3(filters, stride)(x)\n    y = layers.BatchNormalization()(y)\n    y = layers.ReLU()(y)\n    y = conv3x3(filters, 1)(y)\n    y = layers.BatchNormalization()(y)\n\n    # projection if shape changes\n    if x.shape[-1] != filters or stride != 1:\n        x = layers.Conv2D(\n            filters, 1, strides=stride, use_bias=False,\n            kernel_initializer=\"he_normal\",\n            kernel_regularizer=regularizers.l2(WEIGHT_DECAY)\n        )(x)\n        x = layers.BatchNormalization()(x)\n\n    out = layers.Add()([x, y])\n    out = layers.ReLU()(out)\n    return out\n\ndef make_layer(x, filters, blocks, stride):\n    x = basic_block(x, filters, stride)\n    for _ in range(1, blocks):\n        x = basic_block(x, filters, 1)\n    return x\n\ndef build_resnet18(input_shape=(32,32,3), num_classes=10):\n    inputs = layers.Input(shape=input_shape)\n\n    # CIFAR stem (3x3 conv)\n    x = layers.Conv2D(\n        64, 3, padding=\"same\", use_bias=False,\n        kernel_initializer=\"he_normal\",\n        kernel_regularizer=regularizers.l2(WEIGHT_DECAY)\n    )(inputs)\n    x = layers.BatchNormalization()(x)\n    x = layers.ReLU()(x)\n\n    # 4 stages\n    x = make_layer(x,  64, 2, stride=1)  # 32x32\n    x = make_layer(x, 128, 2, stride=2)  # 16x16\n    x = make_layer(x, 256, 2, stride=2)  # 8x8\n    x = make_layer(x, 512, 2, stride=2)  # 4x4\n\n    x = layers.GlobalAveragePooling2D()(x)\n    logits = layers.Dense(num_classes, activation=None,\n                          kernel_regularizer=regularizers.l2(WEIGHT_DECAY))(x)\n    outputs = layers.Softmax(dtype=\"float32\")(logits)  # numerically stable\n    return keras.Model(inputs, outputs, name=\"ResNet18_CIFAR\")\n\nmodel = build_resnet18()\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T15:41:53.463395Z","iopub.execute_input":"2025-09-20T15:41:53.464125Z","iopub.status.idle":"2025-09-20T15:41:53.775582Z","shell.execute_reply.started":"2025-09-20T15:41:53.464096Z","shell.execute_reply":"2025-09-20T15:41:53.775051Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==== Compile & Train ResNet18 on CIFAR-10 ====\nfrom tensorflow import keras\nfrom tensorflow.keras import callbacks\n\n# 1) Optimizer / loss / metric\nopt = keras.optimizers.SGD(learning_rate=0.1, momentum=0.9, nesterov=True)\nmodel.compile(\n    optimizer=opt,\n    loss=keras.losses.SparseCategoricalCrossentropy(),  # no label smoothing\n    metrics=[\"accuracy\"],\n)\n\n# 2) Callbacks (save the best model by val_accuracy)\nckpt_path = \"/kaggle/working/resnet18_best.keras\"\ncbs = [\n    callbacks.ModelCheckpoint(\n        ckpt_path, monitor=\"val_accuracy\", mode=\"max\",\n        save_best_only=True, verbose=1\n    ),\n    callbacks.ReduceLROnPlateau(\n        monitor=\"val_loss\", factor=0.2, patience=5,\n        min_lr=1e-5, verbose=1\n    ),\n    callbacks.EarlyStopping(\n        monitor=\"val_loss\", patience=12,\n        restore_best_weights=True, verbose=1\n    ),\n]\n\n# 3) Train\nEPOCHS = 60  # we can increase later if needed\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=cbs,\n    verbose=1,\n)\n\n# 4) Quick test-set estimate (should be close to Kaggle score)\ntest_loss, test_acc = model.evaluate(test_ds, verbose=0)\nprint(f\"\\n[Official CIFAR-10 test] accuracy={test_acc:.4f}  loss={test_loss:.4f}\")\nprint(f\"Best model saved to: {ckpt_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T15:48:26.262220Z","iopub.execute_input":"2025-09-20T15:48:26.262501Z","iopub.status.idle":"2025-09-20T16:10:46.356864Z","shell.execute_reply.started":"2025-09-20T15:48:26.262482Z","shell.execute_reply":"2025-09-20T16:10:46.356124Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- Build ordered test set, run inference, and write submission.csv ---\n\nimport os, pandas as pd, tensorflow as tf\n\n# 1) Paths (we extracted test images to /kaggle/working/test)\nTEST_DIR = \"/kaggle/working/test\"\nassert os.path.isdir(TEST_DIR), f\"Test folder not found: {TEST_DIR}\"\n\n# 2) Read the sample submission to get the exact required order\nsub = pd.read_csv(\"/kaggle/input/cifar-10/sampleSubmission.csv\")\n\n# Make sure ids are ints, then build string filepaths in the right order\nsub[\"id\"] = sub[\"id\"].astype(int)\ntest_paths = [f\"{TEST_DIR}/{i}.png\" for i in sub[\"id\"]]\n\n# Quick sanity check\nmissing = sum(not os.path.exists(p) for p in test_paths)\nprint(f\"Found test images (existing paths): {len(test_paths) - missing}/{len(test_paths)}\")\nassert missing == 0, \"Some test image paths were not found. Check TEST_DIR.\"\n\n# 3) TF Dataset for test (NO augmentation; just decode + normalize to [0,1])\nAUTO = tf.data.AUTOTUNE\nIMG_SIZE = 32\n\ndef load_and_preprocess(path):\n    img = tf.io.read_file(path)\n    img = tf.image.decode_png(img, channels=3)\n    img = tf.image.convert_image_dtype(img, tf.float32)  # [0,1]\n    return img\n\ntest_ds = (tf.data.Dataset\n           .from_tensor_slices(tf.constant(test_paths))   # <-- STRING tensors\n           .map(load_and_preprocess, num_parallel_calls=AUTO)\n           .batch(256)\n           .prefetch(AUTO))\n\n# 4) Predict\nCLASS_NAMES = ['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck']\nprobs = model.predict(test_ds, verbose=1)\npred_ids = tf.argmax(probs, axis=1).numpy()\nsub[\"label\"] = [CLASS_NAMES[i] for i in pred_ids]\n\n# 5) Save submission\nout_path = \"/kaggle/working/submission.csv\"\nsub.to_csv(out_path, index=False)\nprint(sub.head())\nprint(f\"\\nSubmission saved to: {out_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T17:17:44.230214Z","iopub.execute_input":"2025-09-20T17:17:44.230523Z","iopub.status.idle":"2025-09-20T17:18:25.617736Z","shell.execute_reply.started":"2025-09-20T17:17:44.230499Z","shell.execute_reply":"2025-09-20T17:18:25.616949Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1) Where things are\nCOMP_DIR   = \"/kaggle/input/cifar-10\"              # competition input\nTEST_7Z    = f\"{COMP_DIR}/test.7z\"\nTEST_DIR   = \"/kaggle/working/test\"                 # we will create this\nSAMPLE_CSV = f\"{COMP_DIR}/sampleSubmission.csv\"\n\n# 2) Install extractor (safe to rerun)\n!apt -yqq install libarchive-dev >/dev/null\n!pip -q install libarchive >/dev/null\n\n# 3) Extract test.7z -> /kaggle/working/test  (only if missing)\nimport os, glob, libarchive.public\n\nif not os.path.isdir(TEST_DIR):\n    cnt = 0\n    for _ in libarchive.public.file_pour(TEST_7Z):\n        cnt += 1\n        if cnt % 10000 == 0:\n            print(\"Extracted:\", cnt)\n    print(\"Extraction finished.\")\nelse:\n    print(\"Folder 'test' already exists; skipping.\")\n\nprint(\"Files in test/:\", len(glob.glob(TEST_DIR + \"/*\")))\nprint(\"Paths OK:\", os.path.isdir(TEST_DIR), \"->\", TEST_DIR)\nprint(\"Sample CSV:\", SAMPLE_CSV)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-20T17:11:11.483410Z","iopub.execute_input":"2025-09-20T17:11:11.483759Z","iopub.status.idle":"2025-09-20T17:11:17.411473Z","shell.execute_reply.started":"2025-09-20T17:11:11.483731Z","shell.execute_reply":"2025-09-20T17:11:17.410627Z"}},"outputs":[],"execution_count":null}]}