{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":21154,"databundleVersionId":1243559,"sourceType":"competition"}],"dockerImageVersionId":31234,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os, random, math, json\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\n\ntf.keras.backend.clear_session()\nSEED = 42\ntf.random.set_seed(SEED); np.random.seed(SEED); random.seed(SEED)\n\ntry:\n    tf.config.optimizer.set_experimental_options({\n        \"layout_optimizer\": False,   \n        \"remapping\": True,\n        \"constant_folding\": True\n    })\nexcept Exception as e:\n    print(\"Optimizer experimental options not set:\", e)\n\ntry: tf.config.optimizer.set_jit(False)\nexcept: pass\n\nfor g in tf.config.list_physical_devices(\"GPU\"):\n    try: tf.config.experimental.set_memory_growth(g, True)\n    except: pass\n\nprint(\"TF:\", tf.__version__)\nprint(\"GPUs:\", [tf.config.experimental.get_device_details(x).get(\"device_name\",\"\")\n               for x in tf.config.list_physical_devices(\"GPU\")])\n\ntf.keras.mixed_precision.set_global_policy(\"float32\")\nprint(\"Policy:\", tf.keras.mixed_precision.global_policy())\n\nNUM_CLASSES = 104\nIMG_SIZE    = 224\nDATA_DIR    = f\"/kaggle/input/tpu-getting-started/tfrecords-jpeg-{IMG_SIZE}x{IMG_SIZE}\"\nTRAIN_GLOB  = os.path.join(DATA_DIR, \"train\", \"*.tfrec\")\nVAL_GLOB    = os.path.join(DATA_DIR, \"val\",   \"*.tfrec\")\nTEST_GLOB   = os.path.join(DATA_DIR, \"test\",  \"*.tfrec\")\n\nGLOBAL_BATCH = 64  \nEPOCHS = 30\nAUTO = tf.data.AUTOTUNE\n\nprint(\"DATA_DIR:\", DATA_DIR, \"BATCH:\", GLOBAL_BATCH)\n\ndef _parse_train_val(ex):\n    ex = tf.io.parse_single_example(ex, {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"class\": tf.io.FixedLenFeature([], tf.int64),\n        \"id\":    tf.io.FixedLenFeature([], tf.string)\n    })\n    img = tf.image.decode_jpeg(ex[\"image\"], channels=3)\n    img = tf.image.convert_image_dtype(img, tf.float32)     \n    img = tf.image.resize(img, (IMG_SIZE, IMG_SIZE))\n    img = tf.clip_by_value(img, 0.0, 1.0)                    \n    y   = tf.one_hot(tf.cast(ex[\"class\"], tf.int32), NUM_CLASSES)\n    return img, y\n\ndef _parse_test(ex):\n    ex = tf.io.parse_single_example(ex, {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"id\":    tf.io.FixedLenFeature([], tf.string)\n    })\n    img = tf.image.decode_jpeg(ex[\"image\"], channels=3)\n    img = tf.image.convert_image_dtype(img, tf.float32)\n    img = tf.image.resize(img, (IMG_SIZE, IMG_SIZE))\n    img = tf.clip_by_value(img, 0.0, 1.0)\n    return img, ex[\"id\"]\n\ndef aug(img):\n    img = tf.image.random_flip_left_right(img)\n    img = tf.image.random_brightness(img, 0.08)\n    img = tf.image.random_contrast(img, 0.9, 1.1)\n    return tf.clip_by_value(img, 0.0, 1.0)\n\ndef map_aug(img, y):\n    return aug(img), y\n\ndef speed_opts(ds):\n    opt = tf.data.Options()\n    opt.experimental_deterministic = False\n    opt.threading.private_threadpool_size = 32\n    opt.threading.max_intra_op_parallelism = 1\n    opt.experimental_slack = True\n    return ds.with_options(opt)\n\ndef make_train_ds(pattern, batch=GLOBAL_BATCH):\n    files = tf.io.gfile.glob(pattern)\n    ds = tf.data.TFRecordDataset(files, num_parallel_reads=AUTO)\n    ds = ds.shuffle(8192, seed=SEED, reshuffle_each_iteration=True)\n    ds = ds.map(_parse_train_val, num_parallel_calls=AUTO)\n    ds = ds.map(map_aug,         num_parallel_calls=AUTO)\n    ds = ds.batch(batch, drop_remainder=True)\n    ds = ds.prefetch(AUTO)\n    return speed_opts(ds)\n\ndef make_val_ds(pattern, batch=GLOBAL_BATCH):\n    files = tf.io.gfile.glob(pattern)\n    ds = tf.data.TFRecordDataset(files, num_parallel_reads=AUTO)\n    ds = ds.map(_parse_train_val, num_parallel_calls=AUTO)\n    ds = ds.batch(batch, drop_remainder=False)\n    ds = ds.prefetch(AUTO)\n    return speed_opts(ds)\n\ndef make_test_ds(pattern, batch=GLOBAL_BATCH):\n    files = tf.io.gfile.glob(pattern)\n    ds = tf.data.TFRecordDataset(files, num_parallel_reads=AUTO)\n    ds = ds.map(_parse_test, num_parallel_calls=AUTO)\n    ds = ds.batch(batch, drop_remainder=False)\n    ds = ds.prefetch(AUTO)\n    return speed_opts(ds)\n\ntrain_ds = make_train_ds(TRAIN_GLOB)\nval_ds   = make_val_ds(VAL_GLOB)\ntest_ds  = make_test_ds(TEST_GLOB)\n\nfrom tensorflow.keras import layers, models, regularizers\nL2 = regularizers.l2(1e-5)\n\ndef bn_swish(x):\n    x = layers.BatchNormalization(epsilon=1e-3, momentum=0.99)(x)\n    return layers.Activation('swish')(x)\n\ndef se_block(x, r=8):\n    c = x.shape[-1]\n    s = layers.GlobalAveragePooling2D()(x)\n    s = layers.Dense(max(c//r, 8), activation='swish', kernel_regularizer=L2)(s)\n    s = layers.Dense(c, activation='sigmoid', dtype='float32', kernel_regularizer=L2)(s)\n    s = layers.Reshape((1,1,c))(s)\n    return layers.Multiply()([x, s])\n\ndef sep_block(x, out_ch, stride=1, drop=0.0):\n    y = layers.DepthwiseConv2D(3, strides=stride, padding='same', use_bias=False, depthwise_regularizer=L2)(x)\n    y = bn_swish(y)\n    y = layers.Conv2D(out_ch, 1, padding='same', use_bias=False, kernel_regularizer=L2)(y)\n    y = bn_swish(y)\n    y = se_block(y)\n    if drop>0: y = layers.Dropout(drop)(y)\n    return y\n\ndef build_tinysepnet(img_size=IMG_SIZE, num_classes=NUM_CLASSES, width=1.25):\n    inp = layers.Input((img_size, img_size, 3))\n    x = layers.Conv2D(int(32*width), 3, strides=2, padding='same', use_bias=False, kernel_regularizer=L2)(inp)\n    x = bn_swish(x)\n\n    stage = [(int(48*width), 2),\n             (int(64*width), 2),\n             (int(96*width), 2),\n             (int(128*width),2)]\n    for i,(c, blocks) in enumerate(stage):\n        for j in range(blocks):\n            stride = 2 if (i>0 and j==0) else 1\n            x = sep_block(x, c, stride=stride, drop=0.05)\n\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dropout(0.25)(x)\n    out = layers.Dense(num_classes, activation='softmax', dtype='float32', kernel_regularizer=L2)(x)\n    return models.Model(inp, out)\n\nmodel = build_tinysepnet()\nmodel.summary()\n\noptimizer = tf.keras.optimizers.Adam(learning_rate=1e-3, clipnorm=1.0)\nloss      = tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.0)\nmodel.compile(optimizer=optimizer, loss=loss, metrics=['accuracy'])  \n\nckpt = tf.keras.callbacks.ModelCheckpoint(\n    \"best_tinysepnet_multi.weights.h5\", monitor=\"val_loss\",\n    save_best_only=True, save_weights_only=True, verbose=1\n)\nes  = tf.keras.callbacks.EarlyStopping(monitor=\"val_loss\", patience=6, restore_best_weights=True, verbose=1)\nrlr = tf.keras.callbacks.ReduceLROnPlateau(monitor=\"val_loss\", factor=0.5, patience=2, min_lr=2e-5, verbose=1)\n\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=[ckpt, es, rlr],\n    verbose=2\n)\n\ndef tta_probs(model, imgs):\n    views = [\n        imgs,\n        tf.image.flip_left_right(imgs),\n        tf.image.central_crop(imgs, 0.9),\n        tf.image.central_crop(imgs, 0.8),\n    ]\n    acc = None\n    for v in views:\n        p = model.predict(v, verbose=0)\n        acc = p if acc is None else acc + p\n    return acc / len(views)\n\nmodel.load_weights(\"best_tinysepnet_multi.weights.h5\")\n\nids, probs = [], []\nfor xb, ib in test_ds:\n    p = tta_probs(model, xb)\n    probs.append(p)\n    ids.extend([b.numpy().decode(\"utf-8\") for b in ib])\nprobs = np.vstack(probs)\nlabels = np.argmax(probs, axis=1).astype(int)\n\nsub = pd.DataFrame({\"id\": ids, \"label\": labels})\nsub.to_csv(\"submission.csv\", index=False)\nprint(sub.head(), \"\\nSaved to: submission.csv\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-23T08:25:58.284088Z","iopub.execute_input":"2025-12-23T08:25:58.284637Z","iopub.status.idle":"2025-12-23T08:43:47.387347Z","shell.execute_reply.started":"2025-12-23T08:25:58.284606Z","shell.execute_reply":"2025-12-23T08:43:47.386471Z"}},"outputs":[],"execution_count":null}]}