{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"https://www.kaggle.com/code/jarallaalmarri/notebook956b4e8546","metadata":{}},{"cell_type":"code","source":"\"\"\"\nCIFAR-10 Kaggle competition - fast version\n==========================================\nSame ResNet-9 as before (trained from scratch, no pre-trained weights),\nbut the 300,000 Kaggle test images are read STRAIGHT OUT OF test.7z in\nmemory instead of being extracted to disk.\n\nWriting 300k tiny files to the Kaggle disk is what took ~4 hours.\nStreaming them takes a few minutes, so the whole notebook runs in\nroughly 10-15 minutes.\n\nKaggle settings needed:\n  - Add Input -> Competitions -> CIFAR-10 - Object Recognition in Images\n  - Accelerator: GPU T4 x2\n  - Internet: ON (needed to download the official CIFAR-10 training set)\n\"\"\"\n\nimport os\nimport io\nimport sys\nimport glob\nimport time\nimport itertools\nimport subprocess\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nimport keras\nfrom keras import layers\nfrom PIL import Image\n\nkeras.utils.set_random_seed(42)\nkeras.mixed_precision.set_global_policy(\"mixed_float16\")   # ~2x faster on T4\n\nprint(\"TensorFlow\", tf.__version__, \"| Keras\", keras.__version__)\nprint(\"GPUs:\", tf.config.list_physical_devices(\"GPU\"))\n\nCLASSES = [\"airplane\", \"automobile\", \"bird\", \"cat\", \"deer\",\n           \"dog\", \"frog\", \"horse\", \"ship\", \"truck\"]\nAUTOTUNE = tf.data.AUTOTUNE\n\n# ---------------------------------------------------------------\n# 1. Training data: official CIFAR-10 (identical to the Kaggle train set)\n# ---------------------------------------------------------------\n(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()\ny_train = y_train.ravel()\ny_test = y_test.ravel()\nprint(\"Train:\", x_train.shape, \"Test:\", x_test.shape)\n\n# ---------------------------------------------------------------\n# 2. Augmentation: random crop, horizontal flip, cutout\n# ---------------------------------------------------------------\nBATCH = 512\nEPOCHS = 30\n\n\ndef cutout(img, size=8):\n    cy = tf.random.uniform([], 0, 32, dtype=tf.int32)\n    cx = tf.random.uniform([], 0, 32, dtype=tf.int32)\n    yy = tf.range(32)[:, None]\n    xx = tf.range(32)[None, :]\n    inside = ((yy >= cy - size // 2) & (yy < cy + size // 2) &\n              (xx >= cx - size // 2) & (xx < cx + size // 2))\n    mask = tf.cast(tf.logical_not(inside), img.dtype)[..., None]\n    return img * mask\n\n\ndef augment(img, label):\n    img = tf.pad(img, [[4, 4], [4, 4], [0, 0]], mode=\"REFLECT\")\n    img = tf.image.random_crop(img, [32, 32, 3])\n    img = tf.image.random_flip_left_right(img)\n    img = cutout(img)\n    return tf.cast(img, tf.float32), tf.one_hot(label, 10)\n\n\ndef to_eval(img, label):\n    return tf.cast(img, tf.float32), tf.one_hot(label, 10)\n\n\ntrain_ds = (tf.data.Dataset.from_tensor_slices((x_train, y_train))\n            .shuffle(50000, reshuffle_each_iteration=True)\n            .map(augment, num_parallel_calls=AUTOTUNE)\n            .batch(BATCH, drop_remainder=True)\n            .prefetch(AUTOTUNE))\n\nval_ds = (tf.data.Dataset.from_tensor_slices((x_test, y_test))\n          .map(to_eval, num_parallel_calls=AUTOTUNE)\n          .batch(1000)\n          .prefetch(AUTOTUNE))\n\n# ---------------------------------------------------------------\n# 3. Model: ResNet-9, all weights randomly initialised\n# ---------------------------------------------------------------\nMEAN = np.array([0.4914, 0.4822, 0.4465])\nSTD = np.array([0.2470, 0.2435, 0.2616])\nREG = keras.regularizers.L2(2.5e-4)\n\n\ndef conv_bn(x, filters, pool=False):\n    x = layers.Conv2D(filters, 3, padding=\"same\", use_bias=False,\n                      kernel_regularizer=REG)(x)\n    x = layers.BatchNormalization(momentum=0.9, epsilon=1e-5)(x)\n    x = layers.ReLU()(x)\n    if pool:\n        x = layers.MaxPooling2D(2)(x)\n    return x\n\n\ndef residual(x, filters):\n    y = conv_bn(x, filters)\n    y = conv_bn(y, filters)\n    return layers.Add()([x, y])\n\n\ninputs = keras.Input(shape=(32, 32, 3))\nx = layers.Rescaling(1.0 / 255)(inputs)\nx = layers.Normalization(mean=MEAN, variance=STD ** 2)(x)\n\nx = conv_bn(x, 64)\nx = conv_bn(x, 128, pool=True)\nx = residual(x, 128)\nx = conv_bn(x, 256, pool=True)\nx = conv_bn(x, 512, pool=True)\nx = residual(x, 512)\n\nx = layers.GlobalMaxPooling2D()(x)\nx = layers.Dense(10, use_bias=False, kernel_regularizer=REG)(x)\nx = layers.Rescaling(0.125)(x)\noutputs = layers.Activation(\"softmax\", dtype=\"float32\")(x)\n\nmodel = keras.Model(inputs, outputs, name=\"resnet9\")\nmodel.summary()\n\n\n# ---------------------------------------------------------------\n# 4. One-cycle learning rate + SGD\n# ---------------------------------------------------------------\nclass OneCycle(keras.optimizers.schedules.LearningRateSchedule):\n    def __init__(self, peak, total_steps, warm_frac=0.2):\n        self.peak = peak\n        self.total_steps = total_steps\n        self.warm_frac = warm_frac\n\n    def __call__(self, step):\n        step = keras.ops.cast(step, \"float32\")\n        warm = self.total_steps * self.warm_frac\n        up = self.peak * step / warm\n        down = self.peak * keras.ops.maximum(\n            0.0, (self.total_steps - step) / (self.total_steps - warm))\n        return keras.ops.where(step < warm, up, down)\n\n    def get_config(self):\n        return {\"peak\": self.peak, \"total_steps\": self.total_steps,\n                \"warm_frac\": self.warm_frac}\n\n\nsteps_per_epoch = len(x_train) // BATCH\nmodel.compile(\n    optimizer=keras.optimizers.SGD(\n        learning_rate=OneCycle(peak=0.4, total_steps=EPOCHS * steps_per_epoch),\n        momentum=0.9, nesterov=True),\n    loss=keras.losses.CategoricalCrossentropy(label_smoothing=0.2),\n    metrics=[\"accuracy\"],\n)\n\n# ---------------------------------------------------------------\n# 5. Train (~5 minutes on a T4)\n# ---------------------------------------------------------------\nt0 = time.time()\nhistory = model.fit(train_ds, validation_data=val_ds, epochs=EPOCHS, verbose=2)\ntrain_minutes = (time.time() - t0) / 60\nprint(f\"\\nTraining time: {train_minutes:.1f} minutes\")\n\n\n# ---------------------------------------------------------------\n# 6. Offline score estimate (test-time augmentation)\n# ---------------------------------------------------------------\ndef tta_predict_array(arr, batch=2000):\n    \"\"\"Average predictions of each image and its mirror.\"\"\"\n    out = np.zeros((len(arr), 10), np.float32)\n    for i in range(0, len(arr), batch):\n        chunk = arr[i:i + batch].astype(\"float32\")\n        p = model.predict(chunk, verbose=0)\n        p += model.predict(chunk[:, :, ::-1, :], verbose=0)\n        out[i:i + batch] = p / 2\n    return out\n\n\ntest_probs = tta_predict_array(x_test)\ntest_acc = (test_probs.argmax(axis=1) == y_test).mean()\nprint(f\"Official CIFAR-10 test accuracy (with TTA): {test_acc * 100:.2f}%\")\nprint(\"-> expected Kaggle score is about\", round(test_acc, 4))\n\n# ---------------------------------------------------------------\n# 7. Read the Kaggle test set STRAIGHT FROM test.7z (no disk extraction)\n# ---------------------------------------------------------------\narchives = glob.glob(\"/kaggle/input/**/test.7z\", recursive=True)\nif not archives:\n    raise FileNotFoundError(\n        \"test.7z not found. Add Input -> Competitions -> CIFAR-10.\")\nARCHIVE = archives[0]\nCOMP_DIR = os.path.dirname(ARCHIVE)\n\nsub = pd.read_csv(os.path.join(COMP_DIR, \"sampleSubmission.csv\"))\nN = len(sub)\nprint(\"Submission rows:\", N)\n\nimages = np.zeros((N, 32, 32, 3), np.uint8)   # ~0.9 GB, fits easily in RAM\nfound = np.zeros(N, bool)\n\n\ndef stream_with_libarchive(path):\n    \"\"\"Yield (filename, png_bytes) without writing anything to disk.\"\"\"\n    try:\n        import libarchive.public\n    except ImportError:\n        print(\"installing libarchive ...\")\n        subprocess.run(\"apt-get -qq update && apt-get -qq install -y libarchive-dev\",\n                       shell=True, stdout=subprocess.DEVNULL)\n        subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"libarchive\"],\n                       check=True)\n        import libarchive.public\n    with libarchive.public.file_reader(path) as reader:\n        for entry in reader:\n            name = str(entry.pathname)\n            if name.lower().endswith(\".png\"):\n                yield name, b\"\".join(entry.get_blocks())\n\n\ndef stream_with_py7zr(path):\n    \"\"\"Fallback reader, also entirely in memory.\"\"\"\n    try:\n        import py7zr\n    except ImportError:\n        subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"py7zr\"],\n                       check=True)\n        import py7zr\n    with py7zr.SevenZipFile(path, mode=\"r\") as z:\n        for name, bio in z.readall().items():\n            if name.lower().endswith(\".png\"):\n                yield name, bio.read()\n\n\nt0 = time.time()\ncount = 0\ntry:\n    stream = stream_with_libarchive(ARCHIVE)\n    first = next(stream)                      # fail fast if libarchive breaks\n    stream = itertools.chain([first], stream)\nexcept Exception as e:                        # noqa: BLE001\n    print(\"libarchive unavailable, falling back to py7zr:\", e)\n    stream = stream_with_py7zr(ARCHIVE)\n\nfor name, data in stream:\n    idx = int(os.path.splitext(os.path.basename(name))[0]) - 1   # ids are 1..N\n    if 0 <= idx < N:\n        images[idx] = np.asarray(Image.open(io.BytesIO(data)).convert(\"RGB\"))\n        found[idx] = True\n    count += 1\n    if count % 50000 == 0:\n        print(f\"  {count} images read, {time.time() - t0:.0f}s elapsed\")\n\nprint(f\"Read {count} images in {time.time() - t0:.0f}s\")\nprint(\"Missing images:\", int((~found).sum()))     # must be 0\n\n# ---------------------------------------------------------------\n# 8. Predict and write submission.csv\n# ---------------------------------------------------------------\nt0 = time.time()\nkaggle_probs = tta_predict_array(images)\nprint(f\"Predicted {N} images in {time.time() - t0:.0f}s\")\n\nsub[\"label\"] = [CLASSES[i] for i in kaggle_probs.argmax(axis=1)]\nsub.to_csv(\"/kaggle/working/submission.csv\", index=False)\nprint(sub.head())\nprint(\"\\nsubmission.csv written to /kaggle/working/\")\n\n# ---------------------------------------------------------------\n# 9. Training curves\n# ---------------------------------------------------------------\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4))\nax1.plot(history.history[\"accuracy\"], label=\"train\")\nax1.plot(history.history[\"val_accuracy\"], label=\"test\")\nax1.set_xlabel(\"epoch\"); ax1.set_ylabel(\"accuracy\"); ax1.set_title(\"Accuracy\"); ax1.legend()\nax2.plot(history.history[\"loss\"], label=\"train\")\nax2.plot(history.history[\"val_loss\"], label=\"test\")\nax2.set_xlabel(\"epoch\"); ax2.set_ylabel(\"loss\"); ax2.set_title(\"Loss\"); ax2.legend()\nplt.tight_layout()\nplt.show()\n\nprint(f\"\\nSummary: ResNet-9, {model.count_params():,} parameters, \"\n      f\"{EPOCHS} epochs, {train_minutes:.1f} min training, \"\n      f\"official test accuracy {test_acc * 100:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-22T08:28:13.629234Z","iopub.execute_input":"2026-09-22T08:28:13.629532Z"}},"outputs":[],"execution_count":null}]}