{"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,"sourceType":"competition"}],"dockerImageVersionId":31194,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install --no-deps protobuf==3.20.3","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T09:47:54.372802Z","iopub.execute_input":"2025-12-03T09:47:54.373137Z","iopub.status.idle":"2025-12-03T09:47:55.931789Z","shell.execute_reply.started":"2025-12-03T09:47:54.373110Z","shell.execute_reply":"2025-12-03T09:47:55.930924Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\nos.environ[\"TF_USE_LEGACY_KERAS\"] = \"1\"\n\nimport tensorflow as tf\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import classification_report, f1_score\nimport re\n\nprint(\"TensorFlow Version:\", tf.__version__)\n\n# GPU Strategy\ntry:\n    gpus = tf.config.list_physical_devices('GPU')\n    if gpus:\n        strategy = tf.distribute.MirroredStrategy()\n        print(f\"GPU Terdeteksi: {len(gpus)} GPU aktif.\")\n    else:\n        strategy = tf.distribute.get_strategy()\n        print(\"Peringatan: Tidak ada GPU, menggunakan CPU.\")\nexcept:\n    strategy = tf.distribute.get_strategy()\n    print(\"Fallback ke default strategy\")\n\n# Global Config\nAUTO = tf.data.experimental.AUTOTUNE\nBATCH_SIZE = 64 * strategy.num_replicas_in_sync\nIMAGE_SIZE = [128, 128]\nEPOCHS = 25\n\nPATH = \"/kaggle/input/tpu-getting-started/tfrecords-jpeg-192x192\"\n\nTRAINING_FILENAMES = tf.io.gfile.glob(PATH + \"/train/*.tfrec\")\nVALIDATION_FILENAMES = tf.io.gfile.glob(PATH + \"/val/*.tfrec\")\nTEST_FILENAMES = tf.io.gfile.glob(PATH + \"/test/*.tfrec\")\n\nprint(\"Train Files :\", len(TRAINING_FILENAMES))\nprint(\"Val Files   :\", len(VALIDATION_FILENAMES))\nprint(\"Test Files  :\", len(TEST_FILENAMES))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T09:47:55.933410Z","iopub.execute_input":"2025-12-03T09:47:55.933665Z","iopub.status.idle":"2025-12-03T09:47:55.968814Z","shell.execute_reply.started":"2025-12-03T09:47:55.933642Z","shell.execute_reply":"2025-12-03T09:47:55.968182Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def decode_image(image_data):\n    img = tf.image.decode_jpeg(image_data, channels=3)\n    img = tf.image.resize(img, IMAGE_SIZE)\n    img = tf.cast(img, tf.float32) / 255.0\n    img = tf.reshape(img, [*IMAGE_SIZE, 3])\n    return img\n\ndef read_labeled_tfrecord(example):\n    feature = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"class\": tf.io.FixedLenFeature([], tf.int64)\n    }\n    example = tf.io.parse_single_example(example, feature)\n    return decode_image(example[\"image\"]), tf.cast(example[\"class\"], tf.int32)\n\ndef read_unlabeled_tfrecord(example):\n    feature = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"id\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, feature)\n    return decode_image(example[\"image\"]), example[\"id\"]\n\n# Wajib (augmentasi rubrik)\ndef augment(image, label):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_brightness(image, 0.1)\n    image = tf.image.random_contrast(image, 0.9, 1.1)\n    return image, label\n\ndef get_training_dataset():\n    dataset = tf.data.TFRecordDataset(TRAINING_FILENAMES, num_parallel_reads=AUTO)\n    dataset = dataset.map(read_labeled_tfrecord, num_parallel_calls=AUTO)\n    dataset = dataset.map(augment, num_parallel_calls=AUTO)\n    dataset = dataset.repeat()\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)\n    return dataset.prefetch(AUTO)\n\ndef get_validation_dataset():\n    dataset = tf.data.TFRecordDataset(VALIDATION_FILENAMES, num_parallel_reads=AUTO)\n    dataset = dataset.map(read_labeled_tfrecord, num_parallel_calls=AUTO)\n    dataset = dataset.batch(BATCH_SIZE)\n    return dataset.prefetch(AUTO)\n\ndef get_test_dataset():\n    dataset = tf.data.TFRecordDataset(TEST_FILENAMES, num_parallel_reads=AUTO)\n    dataset = dataset.map(read_unlabeled_tfrecord, num_parallel_calls=AUTO)\n    dataset = dataset.batch(BATCH_SIZE)\n    return dataset.prefetch(AUTO)\n\ndef count_data(filenames):\n    nums = [int(re.compile(r\"-(\\d+)\\.\").search(f).group(1)) for f in filenames]\n    return np.sum(nums)\n\nNUM_TRAIN = count_data(TRAINING_FILENAMES)\nSTEPS_PER_EPOCH = (NUM_TRAIN // BATCH_SIZE) - 1\n\nprint(\"Total Train Images:\", NUM_TRAIN)\nprint(\"Steps/Epoch:\", STEPS_PER_EPOCH)\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-03T09:47:55.969722Z","iopub.execute_input":"2025-12-03T09:47:55.969903Z","iopub.status.idle":"2025-12-03T09:47:55.981473Z","shell.execute_reply.started":"2025-12-03T09:47:55.969888Z","shell.execute_reply":"2025-12-03T09:47:55.980602Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with strategy.scope():\n    model = tf.keras.Sequential([\n        tf.keras.Input(shape=(*IMAGE_SIZE, 3)),  # FIXED INPUT\n        tf.keras.layers.Flatten(),\n\n        tf.keras.layers.Dense(2048, activation='relu'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.Dropout(0.3),\n\n        tf.keras.layers.Dense(1024, activation='relu'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.Dropout(0.3),\n\n        tf.keras.layers.Dense(512, activation='relu'),\n        tf.keras.layers.Dropout(0.2),\n\n        tf.keras.layers.Dense(104, activation='softmax')  # 104 class bunga\n    ])\n\n    model.compile(\n        optimizer='adam',\n        loss='sparse_categorical_crossentropy',\n        metrics=['sparse_categorical_accuracy']\n    )\n\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T09:47:55.982949Z","iopub.execute_input":"2025-12-03T09:47:55.983405Z","iopub.status.idle":"2025-12-03T09:47:56.242774Z","shell.execute_reply.started":"2025-12-03T09:47:55.983385Z","shell.execute_reply":"2025-12-03T09:47:56.242021Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"lr_cb = tf.keras.callbacks.ReduceLROnPlateau(\n    monitor=\"val_loss\", factor=0.5, patience=2, min_lr=1e-6, verbose=1\n)\n\nes_cb = tf.keras.callbacks.EarlyStopping(\n    monitor=\"val_loss\", patience=6, restore_best_weights=True\n)\n\nhistory = model.fit(\n    get_training_dataset(),\n    steps_per_epoch=STEPS_PER_EPOCH,\n    epochs=EPOCHS,\n    validation_data=get_validation_dataset(),\n    callbacks=[lr_cb, es_cb]\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T09:47:56.243709Z","iopub.execute_input":"2025-12-03T09:47:56.243933Z","iopub.status.idle":"2025-12-03T09:54:21.630648Z","shell.execute_reply.started":"2025-12-03T09:47:56.243916Z","shell.execute_reply":"2025-12-03T09:54:21.630052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_training(history):\n    acc = history.history['sparse_categorical_accuracy']\n    val_acc = history.history['val_sparse_categorical_accuracy']\n    loss = history.history['loss']\n    val_loss = history.history['val_loss']\n    epochs = range(len(acc))\n\n    plt.figure(figsize=(14,6))\n\n    plt.subplot(1,2,1)\n    plt.plot(epochs, acc, label=\"Train Acc\")\n    plt.plot(epochs, val_acc, label=\"Val Acc\")\n    plt.legend()\n    plt.title(\"Accuracy\")\n\n    plt.subplot(1,2,2)\n    plt.plot(epochs, loss, label=\"Train Loss\")\n    plt.plot(epochs, val_loss, label=\"Val Loss\")\n    plt.legend()\n    plt.title(\"Loss\")\n\n    plt.show()\n\nplot_training(history)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T09:54:21.632126Z","iopub.execute_input":"2025-12-03T09:54:21.632490Z","iopub.status.idle":"2025-12-03T09:54:21.922816Z","shell.execute_reply.started":"2025-12-03T09:54:21.632464Z","shell.execute_reply":"2025-12-03T09:54:21.922052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_ds = get_test_dataset()\n\ntest_ids = []\ntest_preds = []\n\nfor images, ids in test_ds:\n    p = model.predict(images, verbose=0)\n    preds = np.argmax(p, axis=-1)\n\n    ids_clean = [x.decode(\"utf-8\") for x in ids.numpy()]\n\n    test_ids.extend(ids_clean)\n    test_preds.extend(preds)\n\nprint(\"Jumlah Prediksi:\", len(test_ids))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T09:54:21.923661Z","iopub.execute_input":"2025-12-03T09:54:21.923909Z","iopub.status.idle":"2025-12-03T09:54:36.020340Z","shell.execute_reply.started":"2025-12-03T09:54:21.923884Z","shell.execute_reply":"2025-12-03T09:54:36.019574Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\nsubmission = pd.DataFrame({\n    \"id\": test_ids,\n    \"label\": test_preds\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)\n\nprint(\"File submission.csv berhasil dibuat!\")\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-03T09:54:36.021232Z","iopub.execute_input":"2025-12-03T09:54:36.021920Z","iopub.status.idle":"2025-12-03T09:54:36.054421Z","shell.execute_reply.started":"2025-12-03T09:54:36.021892Z","shell.execute_reply":"2025-12-03T09:54:36.053559Z"}},"outputs":[],"execution_count":null}]}