{"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":21154,"databundleVersionId":1243559,"isSourceIdPinned":false,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install efficientnet\n!pip install scikit-learn","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T19:34:21.556311Z","iopub.execute_input":"2025-12-08T19:34:21.556590Z","iopub.status.idle":"2025-12-08T19:34:29.412532Z","shell.execute_reply.started":"2025-12-08T19:34:21.556569Z","shell.execute_reply":"2025-12-08T19:34:29.411488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nimport numpy as np\nimport re\nimport random\nimport os\nfrom tensorflow.keras import layers as L\nfrom tensorflow.keras import applications as tf_applications\nfrom tensorflow.keras import callbacks\n\nSEED = 42\nIMAGE_SIZE = [224, 224]\nEPOCHS = 50\nBATCH_SIZE_PER_REPLICA = 16\nNUM_CLASSES = 104\nAUTO = tf.data.AUTOTUNE\n\ntf.keras.backend.clear_session()\nimport gc\ngc.collect()\n\nTRAIN_GLOB = \"/kaggle/input/tpu-getting-started/tfrecords-jpeg-512x512/train/*.tfrec\"\nVAL_GLOB = \"/kaggle/input/tpu-getting-started/tfrecords-jpeg-512x512/val/*.tfrec\"\nTEST_GLOB = \"/kaggle/input/tpu-getting-started/tfrecords-jpeg-512x512/test/*.tfrec\"\n\npolicy = tf.keras.mixed_precision.Policy('float32')\ntf.keras.mixed_precision.set_global_policy(policy)\n\ndef set_seed(seed):\n    np.random.seed(seed)\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    tf.random.set_seed(seed)\n\nset_seed(SEED)\n\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0\n    image = tf.image.resize(image, IMAGE_SIZE)\n    return image\n\ndef read_labeled_tfrecord(example):\n    LABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"class\": tf.io.FixedLenFeature([], tf.int64),\n    }\n    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    label = tf.cast(example['class'], tf.int32)\n    return image, label\n\ndef read_unlabeled_tfrecord(example):\n    UNLABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"id\": tf.io.FixedLenFeature([], tf.string),\n    }\n    example = tf.io.parse_single_example(example, UNLABELED_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    idnum = example['id']\n    return image, idnum\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False\n\n    ds = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO)\n    ds = ds.with_options(ignore_order)\n    ds = ds.map(read_labeled_tfrecord if labeled else read_unlabeled_tfrecord, num_parallel_calls=AUTO)\n    return ds\n\ndef onehot(image, label):\n    return image, tf.one_hot(label, NUM_CLASSES)\n\ndef data_augment(image, label):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_brightness(image, max_delta=0.1)\n    image = tf.image.random_contrast(image, lower=0.9, upper=1.1)\n    return image, label\n\ndef get_training_dataset(filenames):\n    ds = load_dataset(filenames, labeled=True)\n    ds = ds.map(data_augment, num_parallel_calls=AUTO)\n    ds = ds.map(onehot, num_parallel_calls=AUTO)\n    ds = ds.shuffle(2048, reshuffle_each_iteration=True)\n    return ds\n\ndef get_validation_dataset(filenames, ordered=False):\n    ds = load_dataset(filenames, labeled=True, ordered=ordered)\n    ds = ds.map(onehot, num_parallel_calls=AUTO)\n    return ds\n\ndef get_test_dataset(filenames, ordered=True):\n    ds = load_dataset(filenames, labeled=False, ordered=ordered)\n    return ds\n\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)\n\ntrain_files = tf.io.gfile.glob(TRAIN_GLOB)\nval_files = tf.io.gfile.glob(VAL_GLOB)\ntest_files = tf.io.gfile.glob(TEST_GLOB)\n\nNUM_TRAINING_IMAGES = count_data_items(train_files)\nNUM_VALIDATION_IMAGES = count_data_items(val_files)\nNUM_TEST_IMAGES = count_data_items(test_files)\n\nclass F1Score(tf.keras.metrics.Metric):\n    def __init__(self, name='f1_score', **kwargs):\n        super(F1Score, self).__init__(name=name, **kwargs)\n        self.precision = tf.keras.metrics.Precision()\n        self.recall = tf.keras.metrics.Recall()\n\n    def update_state(self, y_true, y_pred, sample_weight=None):\n        y_pred = tf.argmax(y_pred, axis=-1)\n        y_true = tf.argmax(y_true, axis=-1)\n        self.precision.update_state(y_true, y_pred)\n        self.recall.update_state(y_true, y_pred)\n\n    def result(self):\n        p = self.precision.result()\n        r = self.recall.result()\n        return 2 * ((p * r) / (p + r + tf.keras.backend.epsilon()))\n\n    def reset_states(self):\n        self.precision.reset_states()\n        self.recall.reset_states()\n\ndef get_lr_callback():\n    LR_START = 0.00001\n    LR_MAX = 0.00005\n    LR_MIN = 0.00001\n    LR_RAMPUP_EPOCHS = 5\n    LR_SUSTAIN_EPOCHS = 0\n    LR_EXP_DECAY = .8\n\n    def lrfn(epoch):\n        if epoch < LR_RAMPUP_EPOCHS:\n            lr = (LR_MAX - LR_START) / LR_RAMPUP_EPOCHS * epoch + LR_START\n        elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:\n            lr = LR_MAX\n        else:\n            lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY**(epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN\n        return lr\n    \n    return callbacks.LearningRateScheduler(lrfn, verbose=1)\n\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    try:\n        for gpu in gpus:\n            tf.config.experimental.set_memory_growth(gpu, True)\n        logical_gpus = tf.config.list_logical_devices('GPU')\n        print(f\"{len(gpus)} Physical GPUs, {len(logical_gpus)} Logical GPUs\")\n    except RuntimeError as e:\n        print(e)\n\nstrategy = tf.distribute.get_strategy()\nprint(f'Using strategy: {strategy.__class__.__name__}')\n\nGLOBAL_BATCH_SIZE = BATCH_SIZE_PER_REPLICA\nSTEPS_PER_EPOCH = NUM_TRAINING_IMAGES // GLOBAL_BATCH_SIZE\nVALIDATION_STEPS = NUM_VALIDATION_IMAGES // GLOBAL_BATCH_SIZE\n\ndef create_and_fit_model(model_name='densenet'):\n    with strategy.scope():\n        if model_name == 'efficientnet':\n            base_model = tf.keras.applications.EfficientNetB7(\n                weights='noisy-student',\n                include_top=False,\n                pooling='avg',\n                input_shape=(*IMAGE_SIZE, 3)\n            )\n            print(\"Using EfficientNetB7 with pretrained weights\")\n            \n        elif model_name == 'densenet':\n            base_model = tf.keras.applications.DenseNet201(\n                weights='imagenet',\n                include_top=False,\n                pooling='avg',\n                input_shape=(*IMAGE_SIZE, 3)\n            )\n            print(\"Using DenseNet201 with ImageNet weights\")\n        \n        base_model.trainable = False\n        \n        model = tf.keras.Sequential([\n            base_model,\n            L.Dense(512, activation='relu'),\n            L.BatchNormalization(),\n            L.Dropout(0.3),\n            L.Dense(NUM_CLASSES, activation='softmax')\n        ])\n        \n        optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)\n        \n        model.compile(\n            optimizer=optimizer,\n            loss='categorical_crossentropy',\n            metrics=['accuracy', F1Score()]\n        )\n        \n        model.summary()\n    \n    train_ds = get_training_dataset(train_files)\n    train_ds = train_ds.repeat().batch(GLOBAL_BATCH_SIZE).prefetch(AUTO)\n    \n    val_ds = get_validation_dataset(val_files, ordered=True)\n    val_ds = val_ds.batch(GLOBAL_BATCH_SIZE).cache().prefetch(AUTO)\n\n    os.makedirs('checkpoints', exist_ok=True)\n    \n    checkpoint = callbacks.ModelCheckpoint(\n        f'checkpoints/{model_name}_best.weights.h5',\n        monitor='val_accuracy',\n        mode='max',\n        save_best_only=True,\n        save_weights_only=True,\n        verbose=1\n    )\n    \n    early_stop = callbacks.EarlyStopping(\n        monitor='val_accuracy',\n        patience=4,\n        restore_best_weights=True,\n        mode='max',\n        verbose=1\n    )\n    \n    reduce_lr = callbacks.ReduceLROnPlateau(\n        monitor='val_accuracy',\n        factor=0.5,\n        patience=2,\n        min_lr=1e-6,\n        mode='max',\n        verbose=1\n    )\n    \n    print(f\"\\nTraining {model_name} (frozen layers)...\")\n    history_frozen = model.fit(\n        train_ds,\n        steps_per_epoch=min(200, STEPS_PER_EPOCH),\n        epochs=20,\n        validation_data=val_ds,\n        validation_steps=min(50, VALIDATION_STEPS),\n        callbacks=[checkpoint, reduce_lr],\n        verbose=1\n    )\n    \n    if model_name == 'densenet':\n        print(f\"\\nTraining {model_name} (unfrozen layers)...\")\n        with strategy.scope():\n            base_model.trainable = True\n            \n            for i, layer in enumerate(base_model.layers):\n                if i < 100:\n                    layer.trainable = False\n            \n            optimizer = tf.keras.optimizers.Adam(\n                learning_rate=0.0001,\n                beta_1=0.9,\n                beta_2=0.999,\n                epsilon=1e-07\n            )\n            \n            model.compile(\n                optimizer=optimizer,\n                loss='categorical_crossentropy',\n                metrics=['accuracy', F1Score()]\n            )\n        \n        history_unfrozen = model.fit(\n            train_ds,\n            steps_per_epoch=STEPS_PER_EPOCH,\n            epochs=EPOCHS,\n            validation_data=val_ds,\n            validation_steps=VALIDATION_STEPS,\n            callbacks=[get_lr_callback(), checkpoint, early_stop, reduce_lr],\n            verbose=1\n        )\n        \n        history = {}\n        for key in history_frozen.history.keys():\n            history[key] = history_frozen.history[key] + history_unfrozen.history[key]\n    else:\n        history = history_frozen.history\n    \n    checkpoint_path = f'checkpoints/{model_name}_best.weights.h5'\n    if os.path.exists(checkpoint_path):\n        print(f\"\\nLoading best weights from {checkpoint_path}\")\n        model.load_weights(checkpoint_path)\n    \n    return model, history\n\nprint(\"Training DenseNet201...\")\nmodel, history = create_and_fit_model('densenet')\n\ndef predict_test(model):\n    test_ds = get_test_dataset(test_files, ordered=True)\n    test_ds = test_ds.batch(GLOBAL_BATCH_SIZE).prefetch(AUTO)\n    \n    all_ids = []\n    all_images = []\n    \n    for images, ids in test_ds:\n        all_images.append(images)\n        all_ids.append(ids)\n    \n    images_tensor = tf.concat(all_images, axis=0)\n    ids_tensor = tf.concat(all_ids, axis=0)\n    \n    print(\"\\nGenerating predictions...\")\n    predictions = model.predict(images_tensor, batch_size=GLOBAL_BATCH_SIZE, verbose=1)\n    pred_labels = np.argmax(predictions, axis=1)\n    \n    return ids_tensor.numpy().astype('U'), pred_labels\n\ntest_ids, predictions = predict_test(model)\n\nimport pandas as pd\nsubmission = pd.DataFrame({\n    'id': test_ids,\n    'label': predictions\n})\n\nprint(f\"\\nSubmission info:\")\nprint(f\"Total predictions: {len(submission)}\")\nprint(f\"Unique IDs: {submission['id'].nunique()}\")\nprint(f\"Label range: {submission['label'].min()} to {submission['label'].max()}\")\n\nsubmission.to_csv('submission.csv', index=False)\nprint(\"\\nSubmission saved to 'submission.csv'\")\nprint(\"\\nFirst 10 predictions:\")\nprint(submission.head(10))\n\nprint(\"\\nModel evaluation on validation set:\")\nval_ds = get_validation_dataset(val_files, ordered=True)\nval_ds = val_ds.batch(GLOBAL_BATCH_SIZE).cache().prefetch(AUTO)\n\nval_loss, val_acc, val_f1 = model.evaluate(val_ds, verbose=0)\nprint(f\"Validation Loss: {val_loss:.4f}\")\nprint(f\"Validation Accuracy: {val_acc:.4f}\")\nprint(f\"Validation F1 Score: {val_f1:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T22:14:40.073909Z","iopub.execute_input":"2025-12-08T22:14:40.074271Z","execution_failed":"2025-12-08T22:22:58.016Z"}},"outputs":[],"execution_count":null}]}