{"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":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Cell 1 - Imports & environment check\nimport sys, os, glob\nprint(\"Python:\", sys.version.splitlines()[0])\ntry:\n    import tensorflow as tf\n    from tensorflow import keras as _tf_keras\n    keras_backend = \"tf\"\n    keras = _tf_keras\n    layers = _tf_keras.layers\n    models = _tf_keras.models\n    print(\"Using tf.keras - TensorFlow version:\", tf.__version__)\nexcept Exception as e_tf:\n    print(\"tf.keras import failed:\", e_tf)\n    try:\n        import keras as _keras\n        keras_backend = \"standalone\"\n        keras = _keras\n        layers = _keras.layers\n        models = _keras.models\n        print(\"Using standalone keras - keras version:\", _keras.__version__)\n    except Exception as e_keras:\n        print(\"standalone keras import failed:\", e_keras)\n        raise RuntimeError(\"Keras import failed - restart the Kaggle session to reset environment.\")\nimport numpy as np\nimport matplotlib.pyplot as plt\nprint(\"matplotlib import OK\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-02T19:15:03.33941Z","iopub.execute_input":"2025-10-02T19:15:03.339684Z","iopub.status.idle":"2025-10-02T19:15:07.860483Z","shell.execute_reply.started":"2025-10-02T19:15:03.33966Z","shell.execute_reply":"2025-10-02T19:15:07.859712Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 2 - Residual block helper (basic layers only)\ndef residual_block(x, filters, stride=1, use_projection=False, name=None):\n    prefix = (name + \"_\") if name else \"\"\n    shortcut = x\n\n    y = layers.Conv2D(filters, 3, strides=stride, padding=\"same\", use_bias=False, name=prefix + \"conv1\")(x)\n    y = layers.BatchNormalization(name=prefix + \"bn1\")(y)\n    y = layers.Activation(\"relu\", name=prefix + \"relu1\")(y)\n\n    y = layers.Conv2D(filters, 3, strides=1, padding=\"same\", use_bias=False, name=prefix + \"conv2\")(y)\n    y = layers.BatchNormalization(name=prefix + \"bn2\")(y)\n\n    x_ch = None\n    try:\n        x_ch = int(x.shape[-1]) if x.shape[-1] is not None else None\n    except Exception:\n        x_ch = None\n\n    if use_projection or stride != 1 or (x_ch is not None and x_ch != filters):\n        shortcut = layers.Conv2D(filters, 1, strides=stride, padding=\"same\", use_bias=False, name=prefix + \"proj_conv\")(x)\n        shortcut = layers.BatchNormalization(name=prefix + \"proj_bn\")(shortcut)\n\n    out = layers.Add(name=prefix + \"add\")([y, shortcut])\n    out = layers.Activation(\"relu\", name=prefix + \"relu_out\")(out)\n    return out","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-02T19:15:11.173441Z","iopub.execute_input":"2025-10-02T19:15:11.174088Z","iopub.status.idle":"2025-10-02T19:15:11.181012Z","shell.execute_reply.started":"2025-10-02T19:15:11.174058Z","shell.execute_reply":"2025-10-02T19:15:11.180043Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 3: Build model \ndef build_resnet_like(input_shape=(32,32,3), num_classes=10):\n    inputs = keras.Input(shape=input_shape, name=\"input_image\")\n    x = layers.Conv2D(32, 3, strides=1, padding=\"same\", use_bias=False, name=\"stem_conv\")(inputs)\n    x = layers.BatchNormalization(name=\"stem_bn\")(x)\n    x = layers.Activation(\"relu\", name=\"stem_relu\")(x)\n\n    x = residual_block(x, 32, stride=1, use_projection=False, name=\"s1b1\")\n    x = residual_block(x, 32, stride=1, use_projection=False, name=\"s1b2\")\n\n    x = residual_block(x, 64, stride=2, use_projection=True, name=\"s2b1\")\n    x = residual_block(x, 64, stride=1, use_projection=False, name=\"s2b2\")\n\n    x = residual_block(x, 128, stride=2, use_projection=True, name=\"s3b1\")\n    x = residual_block(x, 128, stride=1, use_projection=False, name=\"s3b2\")\n\n    x = layers.GlobalAveragePooling2D(name=\"gap\")(x)\n    x = layers.Dense(128, activation=\"relu\", name=\"fc1\")(x)\n    outputs = layers.Dense(num_classes, activation=\"softmax\", name=\"predictions\")(x)\n\n    return keras.Model(inputs=inputs, outputs=outputs, name=\"SimpleResNetLike\")\n\nmodel = build_resnet_like()\ntry:\n    model.summary(line_length=120)\nexcept TypeError:\n    model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-02T19:15:18.095573Z","iopub.execute_input":"2025-10-02T19:15:18.096241Z","iopub.status.idle":"2025-10-02T19:15:19.571921Z","shell.execute_reply.started":"2025-10-02T19:15:18.096212Z","shell.execute_reply":"2025-10-02T19:15:19.571266Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 4 - Load CIFAR-10 (train/official test) and provide Kaggle-test loader function\nfrom tensorflow.keras.datasets import cifar10\nfrom tensorflow.keras.utils import to_categorical\n(x_train, y_train), (x_test, y_test) = cifar10.load_data()\nx_train = x_train.astype(\"float32\") / 255.0\nx_test  = x_test.astype(\"float32\") / 255.0\ny_train = to_categorical(y_train, 10)\ny_test  = to_categorical(y_test, 10)\nprint(\"CIFAR-10 loaded. x_train:\", x_train.shape, \"x_test:\", x_test.shape)\n\n# Kaggle test loader:\n# Set KAGGLE_TEST_DIR to the folder containing unlabeled test images (e.g. '/kaggle/input/cifar-10/test_images')\nKAGGLE_TEST_DIR = \"/kaggle/input/\"  # <-- replace with the precise path under /kaggle/input for your test images if known\n\ndef load_kaggle_test_images(test_dir, target_size=(32,32)):\n    # Collect image filepaths (common extensions)\n    patterns = (\"**/*.png\",\"**/*.jpg\",\"**/*.jpeg\",\"**/*.bmp\")\n    files = []\n    for p in patterns:\n        files += glob.glob(os.path.join(test_dir, p), recursive=True)\n    files = sorted(list(set(files)))\n    if len(files) == 0:\n        raise FileNotFoundError(f\"No images found under {test_dir}. Please set KAGGLE_TEST_DIR to the correct folder under /kaggle/input.\")\n    imgs = []\n    names = []\n    for f in files:\n        img = tf.io.read_file(f)\n        img = tf.image.decode_image(img, channels=3)\n        img = tf.image.resize(img, target_size)\n        img = tf.cast(img, tf.float32) / 255.0\n        imgs.append(img.numpy())\n        names.append(os.path.basename(f))\n    imgs = np.stack(imgs, axis=0)\n    return imgs, names, files","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-02T19:15:25.173116Z","iopub.execute_input":"2025-10-02T19:15:25.173436Z","iopub.status.idle":"2025-10-02T19:15:28.210715Z","shell.execute_reply.started":"2025-10-02T19:15:25.173413Z","shell.execute_reply":"2025-10-02T19:15:28.209894Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 5 - augmentation generator (ImageDataGenerator) and helper to get callbacks\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\ndef get_datagen():\n    return ImageDataGenerator(\n        rotation_range=12,\n        width_shift_range=0.08,\n        height_shift_range=0.08,\n        horizontal_flip=True\n    )\n\ndef get_callbacks(model_checkpoint_path=\"best_model.h5\", early_stop_patience=6):\n    callbacks = []\n    callbacks.append(keras.callbacks.ModelCheckpoint(model_checkpoint_path, save_best_only=True, monitor=\"val_accuracy\", mode=\"max\"))\n    callbacks.append(keras.callbacks.EarlyStopping(monitor=\"val_accuracy\", patience=early_stop_patience, restore_best_weights=True))\n    return callbacks","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-02T19:15:33.596984Z","iopub.execute_input":"2025-10-02T19:15:33.597792Z","iopub.status.idle":"2025-10-02T19:15:33.603851Z","shell.execute_reply.started":"2025-10-02T19:15:33.597764Z","shell.execute_reply":"2025-10-02T19:15:33.603081Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 6 - Compile & train\n# (robust LR assignment + manual loop)\nimport math, time\nfrom tensorflow.keras import backend as K\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras import regularizers\n\n# ---- hyperparameters ----\nBATCH_SIZE = 256\nEPOCHS = 50\nBASE_LR = 0.1\nMOMENTUM = 0.9\nWEIGHT_DECAY = 1e-4\nLABEL_SMOOTH = 0.1\nUSE_MIXUP = True\nMIXUP_ALPHA = 0.2\nPATIENCE = 12\nCHECKPOINT_PATH = \"best_resnet_like.keras\"\nVALIDATION_SUBSET = 5000  # for faster mid-training validation\n\n# ---- helper: mixup ----\ndef mixup_batch(x, y, alpha=MIXUP_ALPHA):\n    if alpha <= 0:\n        return x, y\n    lam = float(np.random.beta(alpha, alpha))\n    idx = np.random.permutation(x.shape[0])\n    x2 = x[idx]\n    y2 = y[idx]\n    x_mix = lam * x + (1.0 - lam) * x2\n    y_mix = lam * y + (1.0 - lam) * y2\n    return x_mix, y_mix\n\n# ---- datagen ----\ndatagen = ImageDataGenerator(\n    rotation_range=12,\n    width_shift_range=0.125,\n    height_shift_range=0.125,\n    horizontal_flip=True,\n    fill_mode=\"reflect\"\n)\n\n# ---- model builder (with simple L2 kernel_regularizer) ----\ndef build_resnet_with_reg(input_shape=(32,32,3), num_classes=10, wd=WEIGHT_DECAY):\n    inputs = keras.Input(shape=input_shape)\n    x = layers.Conv2D(32, 3, strides=1, padding=\"same\", use_bias=False,\n                      kernel_regularizer=regularizers.l2(wd))(inputs)\n    x = layers.BatchNormalization()(x); x = layers.Activation(\"relu\")(x)\n\n    x = residual_block(x, 32, stride=1, use_projection=False, name=\"s1b1\")\n    x = residual_block(x, 32, stride=1, use_projection=False, name=\"s1b2\")\n\n    x = residual_block(x, 64, stride=2, use_projection=True, name=\"s2b1\")\n    x = residual_block(x, 64, stride=1, use_projection=False, name=\"s2b2\")\n\n    x = residual_block(x, 128, stride=2, use_projection=True, name=\"s3b1\")\n    x = residual_block(x, 128, stride=1, use_projection=False, name=\"s3b2\")\n\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dense(128, activation=\"relu\", kernel_regularizer=regularizers.l2(wd))(x)\n    outputs = layers.Dense(num_classes, activation=\"softmax\", kernel_regularizer=regularizers.l2(wd))(x)\n    return keras.Model(inputs=inputs, outputs=outputs, name=\"ResNetLike_reg\")\n\n# ---- LR schedule (cosine annealing) ----\ndef cosine_annealing(epoch, total_epochs=EPOCHS, lr_max=BASE_LR, lr_min=1e-4 * BASE_LR):\n    cos_inner = (math.pi * (epoch % total_epochs)) / total_epochs\n    return float(lr_min + 0.5 * (lr_max - lr_min) * (1.0 + math.cos(cos_inner)))\n\n# ---- build & compile model ----\nmodel = build_resnet_with_reg()\noptimizer = keras.optimizers.SGD(learning_rate=BASE_LR, momentum=MOMENTUM, nesterov=True)\nloss = keras.losses.CategoricalCrossentropy(label_smoothing=LABEL_SMOOTH)\nmodel.compile(optimizer=optimizer, loss=loss, metrics=[\"accuracy\"])\n\n# ---- callbacks (no save_format arg) ----\ncheckpoint_cb = keras.callbacks.ModelCheckpoint(CHECKPOINT_PATH, monitor=\"val_accuracy\", save_best_only=True)\nearlystop_cb = keras.callbacks.EarlyStopping(monitor=\"val_accuracy\", patience=PATIENCE, restore_best_weights=False)\ncbks = [checkpoint_cb, earlystop_cb]\n\n# wire callbacks for manual loop\nfor cb in cbks:\n    cb.set_model(model)\n    cb.on_train_begin()\n\n# ---- training loop using datagen + optional mixup ----\ntrain_x, train_y = x_train, y_train\ndatagen.fit(train_x)\nsteps_per_epoch = max(1, len(train_x) // BATCH_SIZE)\nprint(f\"Training: epochs={EPOCHS}, batch={BATCH_SIZE}, steps_per_epoch={steps_per_epoch}\")\n\nstart_time = time.time()\nbest_val_acc = -1.0\nstopped_early = False\n\nfor epoch in range(EPOCHS):\n    # compute LR for this epoch and ensure it's a float\n    lr = float(cosine_annealing(epoch, total_epochs=EPOCHS, lr_max=BASE_LR))\n    # assign LR robustly:\n    try:\n        # preferred: assign if learning_rate is a Variable or has assign()\n        if hasattr(model.optimizer.learning_rate, \"assign\"):\n            model.optimizer.learning_rate.assign(lr)\n        else:\n            # fallback: try K.set_value (handles many cases)\n            K.set_value(model.optimizer.learning_rate, float(lr))\n    except Exception as e_lr:\n        # final fallback: overwrite attribute (works in many TF versions)\n        try:\n            model.optimizer.learning_rate = lr\n        except Exception:\n            print(\"Warning: could not set optimizer learning rate via assign or K.set_value; continuing with original LR. Err:\", e_lr)\n\n    print(f\"\\nEpoch {epoch+1}/{EPOCHS} - lr={lr:.6f}\")\n    gen = datagen.flow(train_x, train_y, batch_size=BATCH_SIZE, shuffle=True)\n    # train over steps_per_epoch batches\n    for step in range(steps_per_epoch):\n        xb, yb = next(gen)\n        if USE_MIXUP:\n            xb, yb = mixup_batch(xb, yb, alpha=MIXUP_ALPHA)\n        model.train_on_batch(xb, yb)\n\n    # validation & logs (use subset for speed)\n    val_slice = min(VALIDATION_SUBSET, x_test.shape[0])\n    val_loss, val_acc = model.evaluate(x_test[:val_slice], y_test[:val_slice], verbose=0)\n    print(f\" val_loss={val_loss:.4f} val_acc={val_acc:.4f}\")\n\n    logs = {\"val_loss\": val_loss, \"val_accuracy\": val_acc}\n    for cb in cbks:\n        cb.on_epoch_end(epoch, logs)\n\n    # basic early-stop check (ModelCheckpoint saved best; EarlyStopping may request stop via callback state)\n    # Keras EarlyStopping sets model.stop_training when used inside fit; here we emulate by checking callback attributes\n    # If earlystop_cb patience reached, it sets stopped_epoch; we emulate detection by checking if file exists and no improvement?\n    # Simpler: if earlystop_cb.stopped_epoch is set (>0) we break (not all Keras versions set that in manual usage)\n    if hasattr(earlystop_cb, \"stopped_epoch\") and earlystop_cb.stopped_epoch not in (None, 0):\n        print(\"EarlyStopping requested stop (stopped_epoch):\", earlystop_cb.stopped_epoch)\n        stopped_early = True\n        break\n\n# finalize callbacks\nfor cb in cbks:\n    cb.on_train_end()\n\n# attempt to load best checkpoint if saved\ntry:\n    if os.path.exists(CHECKPOINT_PATH):\n        print(\"Loading best checkpoint from\", CHECKPOINT_PATH)\n        model = keras.models.load_model(CHECKPOINT_PATH)\n    else:\n        print(\"No checkpoint file found at\", CHECKPOINT_PATH, \"- continuing with current model weights.\")\nexcept Exception as e:\n    print(\"Could not load checkpoint (continuing with current weights):\", e)\n\n# final evaluate on full official test set\nfinal_res = model.evaluate(x_test, y_test, verbose=2)\nprint(\"FINAL CIFAR-10 test loss, accuracy:\", final_res)\n\n# save final model in native Keras format (.keras) - optional but recommended\ntry:\n    model.save(\"final_resnet_like.keras\")\n    print(\"Saved final model as final_resnet_like.keras\")\nexcept Exception as e:\n    print(\"Save final model failed:\", e)\n\nprint(\"Total training time (s):\", int(time.time() - start_time))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-02T19:15:38.236416Z","iopub.execute_input":"2025-10-02T19:15:38.237116Z","iopub.status.idle":"2025-10-02T19:47:11.500003Z","shell.execute_reply.started":"2025-10-02T19:15:38.237082Z","shell.execute_reply":"2025-10-02T19:47:11.499304Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 7 - Evaluate on official CIFAR-10 test set (estimate expected Kaggle score)\nres = model.evaluate(x_test, y_test, verbose=2)\nprint(\"Official CIFAR-10 test -> loss, accuracy:\", res)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-02T19:48:24.511504Z","iopub.execute_input":"2025-10-02T19:48:24.511889Z","iopub.status.idle":"2025-10-02T19:48:26.222791Z","shell.execute_reply.started":"2025-10-02T19:48:24.511851Z","shell.execute_reply":"2025-10-02T19:48:26.222216Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 8 - Extract and Load model\nimport os, glob, pandas as pd, numpy as np\nimport tensorflow as tf\nfrom tensorflow.keras.utils import img_to_array, load_img\n\n# Paths & params\nEXTRACT_DIR = \"/kaggle/working/cifar10_test\"  # make sure this exists and contains all test images\nMODEL_PATH = \"/kaggle/working/final_resnet_like.keras\"\nTARGET_SIZE = (32,32)\n\n# 1) Load model\nif 'model' not in globals():\n    if os.path.exists(MODEL_PATH):\n        model = tf.keras.models.load_model(MODEL_PATH)\n        print(\"Loaded model from\", MODEL_PATH)\n    else:\n        raise RuntimeError(\"No model in memory and no saved model.\")\n\n# 2) Collect all image paths\nall_paths = []\nfor ext in (\"*.png\",\"*.PNG\",\"*.jpg\",\"*.JPG\",\"*.jpeg\",\"*.JPEG\",\"*.bmp\",\"*.BMP\"):\n    all_paths += glob.glob(os.path.join(EXTRACT_DIR, \"**\", ext), recursive=True)\nall_paths = sorted(list(dict.fromkeys(all_paths)))\nprint(\"Found\", len(all_paths), \"images\")\nif len(all_paths) == 0:\n    raise FileNotFoundError(f\"No images found in {EXTRACT_DIR}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-02T20:18:24.133237Z","iopub.execute_input":"2025-10-02T20:18:24.133559Z","iopub.status.idle":"2025-10-02T20:18:25.011882Z","shell.execute_reply.started":"2025-10-02T20:18:24.133535Z","shell.execute_reply":"2025-10-02T20:18:25.011015Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 9: Predict and create submission\nfrom tensorflow.keras.utils import img_to_array, load_img\nimport pandas as pd\nimport numpy as np\nfrom pathlib import Path\n\n# 1) Load images safely\nX_test = []\nvalid_paths = []\n\nfor p in all_paths:\n    try:\n        img = load_img(p, target_size=TARGET_SIZE)\n        X_test.append(img_to_array(img) / 255.0)\n        valid_paths.append(p)\n    except Exception as e:\n        print(\"Skipped invalid image:\", p, e)\n\nif len(X_test) == 0:\n    raise RuntimeError(\"No valid images found to predict!\")\n\nX_test = np.array(X_test)\nprint(\"Prepared X_test with shape:\", X_test.shape)\nall_paths = valid_paths  # update paths to only valid images\n\n# 2) Predict\npreds = model.predict(X_test, batch_size=256, verbose=1)\npred_labels = np.argmax(preds, axis=1).astype(int)\nfilenames = [os.path.basename(p) for p in all_paths]\n\n# 3) Prepare submission\nsample_path = next(Path(\"/kaggle/input/cifar-10\").rglob(\"sampleSubmission*.csv\"), None)\n\nif sample_path and sample_path.exists():\n    sample_df = pd.read_csv(sample_path)\n    mapping = {fn: lbl for fn, lbl in zip(filenames, pred_labels)}\n\n    if 'filename' in sample_df.columns:\n        sample_df['label'] = sample_df['filename'].map(mapping).fillna(-1).astype(int)\n        out_df = sample_df[['filename','label']]\n    else:\n        out_df = pd.DataFrame({'filename':filenames, 'label':pred_labels})\nelse:\n    out_df = pd.DataFrame({'filename':filenames, 'label':pred_labels})\n\n# 4) Save submission\nOUT_SUB = \"submission_v2.csv\"\nout_df.to_csv(OUT_SUB, index=False)\nprint(f\"Wrote {OUT_SUB} with {len(out_df)} rows\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-02T20:31:19.633781Z","iopub.execute_input":"2025-10-02T20:31:19.634424Z","iopub.status.idle":"2025-10-02T20:31:37.073394Z","shell.execute_reply.started":"2025-10-02T20:31:19.634392Z","shell.execute_reply":"2025-10-02T20:31:37.072548Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 10: CIFAR-10 official test accuracy\nimport os, numpy as np\nfrom tensorflow import keras\ntry:\n    res = model.evaluate(x_test, y_test, verbose=0)\n    print(\"CIFAR-10 official test -> loss, accuracy:\", res)\nexcept Exception:\n    print(\"No x_test/y_test in memory to evaluate. If needed, reload CIFAR-10 and evaluate with:\\n\"\n          \"from tensorflow.keras.datasets import cifar10\\n(x_train,y_train),(x_test,y_test)=cifar10.load_data()\\n# preprocess and then model.evaluate(...)\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-02T20:31:40.571134Z","iopub.execute_input":"2025-10-02T20:31:40.571435Z","iopub.status.idle":"2025-10-02T20:31:42.032745Z","shell.execute_reply.started":"2025-10-02T20:31:40.571413Z","shell.execute_reply":"2025-10-02T20:31:42.032107Z"}},"outputs":[],"execution_count":null}]}