Files
zeavis-edu/Machine_Learning/notebook.ipynb
T
2026-06-11 19:42:02 +00:00

1.9 MiB
Raw Blame History

ZeaVis Edu — Corn Leaf Disease Classifier

Mengklasifikasikan penyakit daun jagung (Bercak Daun, Hawar Daun, Karat Daun, Daun Sehat) menggunakan EfficientNetV2B0 dengan transfer learning.

1. Persiapan Lingkungan

Mengimpor pustaka, mengatur seed, dan mengoptimalkan konfigurasi. Presisi float32 digunakan untuk komputasi. Resolusi gambar: 224x224 (EfficientNetV2B0).

In [12]:
!pip install -r requirements.txt
Requirement already satisfied: tensorflow==2.19.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 1)) (2.19.0)
Requirement already satisfied: tensorflowjs==4.22.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 2)) (4.22.0)
Requirement already satisfied: gdown in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 3)) (6.1.0)
Requirement already satisfied: numpy in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 4)) (2.1.3)
Requirement already satisfied: matplotlib in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 5)) (3.10.9)
Requirement already satisfied: seaborn in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 6)) (0.13.2)
Requirement already satisfied: pillow in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 7)) (12.2.0)
Requirement already satisfied: split-folders in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 8)) (0.6.1)
Requirement already satisfied: scikit-learn in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 9)) (1.9.0)
Requirement already satisfied: tf2onnx in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 10)) (1.17.0)
Requirement already satisfied: onnxruntime in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 11)) (1.26.0)
Requirement already satisfied: absl-py>=1.0.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (2.4.0)
Requirement already satisfied: astunparse>=1.6.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (1.6.3)
Requirement already satisfied: flatbuffers>=24.3.25 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (25.12.19)
Requirement already satisfied: gast!=0.5.0,!=0.5.1,!=0.5.2,>=0.2.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (0.7.0)
Requirement already satisfied: google-pasta>=0.1.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (0.2.0)
Requirement already satisfied: libclang>=13.0.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (18.1.1)
Requirement already satisfied: opt-einsum>=2.3.2 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (3.4.0)
Requirement already satisfied: packaging in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (23.2)
Requirement already satisfied: protobuf!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<6.0.0dev,>=3.20.3 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (5.29.6)
Requirement already satisfied: requests<3,>=2.21.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (2.34.2)
Requirement already satisfied: setuptools in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (82.0.1)
Requirement already satisfied: six>=1.12.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (1.17.0)
Requirement already satisfied: termcolor>=1.1.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (3.3.0)
Requirement already satisfied: typing-extensions>=3.6.6 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (4.15.0)
Requirement already satisfied: wrapt>=1.11.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (2.2.1)
Requirement already satisfied: grpcio<2.0,>=1.24.3 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (1.81.1)
Requirement already satisfied: tensorboard~=2.19.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (2.19.0)
Requirement already satisfied: keras>=3.5.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (3.14.1)
Requirement already satisfied: h5py>=3.11.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (3.14.0)
Requirement already satisfied: ml-dtypes<1.0.0,>=0.5.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow==2.19.0->-r requirements.txt (line 1)) (0.5.4)
Requirement already satisfied: flax>=0.7.2 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.12.7)
Requirement already satisfied: importlib_resources>=5.9.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflowjs==4.22.0->-r requirements.txt (line 2)) (7.1.0)
Requirement already satisfied: jax>=0.4.13 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.10.1)
Requirement already satisfied: jaxlib>=0.4.13 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.10.1)
Requirement already satisfied: tf-keras>=2.13.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflowjs==4.22.0->-r requirements.txt (line 2)) (2.19.0)
Requirement already satisfied: tensorflow-decision-forests>=1.5.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflowjs==4.22.0->-r requirements.txt (line 2)) (1.12.0)
Requirement already satisfied: tensorflow-hub>=0.16.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.16.1)
Requirement already satisfied: charset_normalizer<4,>=2 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from requests<3,>=2.21.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (3.4.7)
Requirement already satisfied: idna<4,>=2.5 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from requests<3,>=2.21.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (3.18)
Requirement already satisfied: urllib3<3,>=1.26 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from requests<3,>=2.21.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (2.7.0)
Requirement already satisfied: certifi>=2023.5.7 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from requests<3,>=2.21.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (2026.5.20)
Requirement already satisfied: markdown>=2.6.8 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorboard~=2.19.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (3.10.2)
Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorboard~=2.19.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (0.7.2)
Requirement already satisfied: werkzeug>=1.0.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorboard~=2.19.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (3.1.8)
Requirement already satisfied: beautifulsoup4 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from gdown->-r requirements.txt (line 3)) (4.15.0)
Requirement already satisfied: filelock in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from gdown->-r requirements.txt (line 3)) (3.29.3)
Requirement already satisfied: tqdm in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from gdown->-r requirements.txt (line 3)) (4.68.2)
Requirement already satisfied: contourpy>=1.0.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from matplotlib->-r requirements.txt (line 5)) (1.3.3)
Requirement already satisfied: cycler>=0.10 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from matplotlib->-r requirements.txt (line 5)) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from matplotlib->-r requirements.txt (line 5)) (4.63.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from matplotlib->-r requirements.txt (line 5)) (1.5.0)
Requirement already satisfied: pyparsing>=3 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from matplotlib->-r requirements.txt (line 5)) (3.3.2)
Requirement already satisfied: python-dateutil>=2.7 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from matplotlib->-r requirements.txt (line 5)) (2.9.0.post0)
Requirement already satisfied: pandas>=1.2 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from seaborn->-r requirements.txt (line 6)) (3.0.3)
Requirement already satisfied: scipy>=1.10.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from scikit-learn->-r requirements.txt (line 9)) (1.17.1)
Requirement already satisfied: joblib>=1.4.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from scikit-learn->-r requirements.txt (line 9)) (1.5.3)
Requirement already satisfied: narwhals>=2.0.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from scikit-learn->-r requirements.txt (line 9)) (2.22.1)
Requirement already satisfied: threadpoolctl>=3.5.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from scikit-learn->-r requirements.txt (line 9)) (3.6.0)
Requirement already satisfied: onnx>=1.14.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tf2onnx->-r requirements.txt (line 10)) (1.21.0)
Requirement already satisfied: wheel<1.0,>=0.23.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from astunparse>=1.6.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (0.45.1)
Requirement already satisfied: msgpack in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (1.2.0)
Requirement already satisfied: optax in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.2.8)
Requirement already satisfied: orbax-checkpoint in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.12.0)
Requirement already satisfied: tensorstore in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.1.84)
Requirement already satisfied: rich>=11.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (15.0.0)
Requirement already satisfied: PyYAML>=5.4.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (6.0.3)
Requirement already satisfied: treescope>=0.1.7 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.1.10)
Requirement already satisfied: namex in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from keras>=3.5.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (0.1.0)
Requirement already satisfied: optree in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from keras>=3.5.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (0.19.1)
Requirement already satisfied: markdown-it-py>=2.2.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from rich>=11.1->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (4.2.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from rich>=11.1->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (2.20.0)
Requirement already satisfied: mdurl~=0.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from markdown-it-py>=2.2.0->rich>=11.1->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.1.2)
Requirement already satisfied: wurlitzer in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow-decision-forests>=1.5.0->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (3.1.1)
Requirement already satisfied: ydf>=0.11.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from tensorflow-decision-forests>=1.5.0->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.16.0)
Requirement already satisfied: markupsafe>=2.1.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from werkzeug>=1.0.1->tensorboard~=2.19.0->tensorflow==2.19.0->-r requirements.txt (line 1)) (3.0.3)
Requirement already satisfied: soupsieve>=1.6.1 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from beautifulsoup4->gdown->-r requirements.txt (line 3)) (2.8.4)
Requirement already satisfied: etils[epath,epy] in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from orbax-checkpoint->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (1.14.0)
Requirement already satisfied: prometheus-client>=0.20.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from orbax-checkpoint->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.25.0)
Requirement already satisfied: aiofiles in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from orbax-checkpoint->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (25.1.0)
Requirement already satisfied: humanize in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from orbax-checkpoint->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (4.15.0)
Requirement already satisfied: simplejson>=3.16.0 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from orbax-checkpoint->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (4.1.1)
Requirement already satisfied: psutil in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from orbax-checkpoint->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (7.2.2)
Requirement already satisfied: uvloop in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from orbax-checkpoint->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (0.22.1)
Requirement already satisfied: fsspec in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from etils[epath,epy]->orbax-checkpoint->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (2026.4.0)
Requirement already satisfied: zipp in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from etils[epath,epy]->orbax-checkpoint->flax>=0.7.2->tensorflowjs==4.22.0->-r requirements.txt (line 2)) (4.1.0)
Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in /home/asephs/ZeaVis-Edu/.venv/lib/python3.12/site-packages (from requests[socks]->gdown->-r requirements.txt (line 3)) (1.7.1)
In [13]:
import os
import shutil
import zipfile
import random
from collections import Counter
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import splitfolders
from PIL import Image

import tensorflow as tf
from tensorflow.keras import layers, models, callbacks
from tensorflow.keras.applications import EfficientNetV2B0
from tensorflow.keras.applications.efficientnet_v2 import preprocess_input
from tensorflow.keras.optimizers import AdamW
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.utils.class_weight import compute_class_weight

# Detect environment
try:
    from google.colab import drive
    IS_COLAB = True
    print("Running on Google Colab")
except ModuleNotFoundError:
    IS_COLAB = False
    print(f"Running locally (TF {tf.__version__}, GPU: {tf.config.list_physical_devices('GPU')})")

tf.keras.mixed_precision.set_global_policy('float32')

# Hyperparams
IMG_SIZE = (224, 224)
BATCH_SIZE = 32
SEED = 42
random.seed(SEED)
np.random.seed(SEED)
tf.random.set_seed(SEED)

AUTOTUNE = tf.data.AUTOTUNE
print(f"Setup OK. IMG={IMG_SIZE}, BATCH={BATCH_SIZE}")
Running locally (TF 2.19.0, GPU: [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')])
Setup OK. IMG=(224, 224), BATCH=32

2. Download dan Ekstraksi Dataset

In [14]:
if IS_COLAB:
    drive.mount('/content/drive')
    archive_path = '/content/drive/MyDrive/jagung/dataset_jagung.zip'
    destination_path = '/content/dataset_jagung.zip'
    extract_path = '/content/dataset'
else:
    import gdown
    base = os.getcwd()
    archive_path = os.path.join(base, 'dataset_jagung.zip')
    destination_path = archive_path
    extract_path = os.path.join(base, 'dataset')
    DRIVE_FILE_ID = "1s0H2lDOQVCixywk5eZXJz2i9jj4JihxJ"
    if not os.path.exists(archive_path):
        print("Downloading from Google Drive...")
        try:
            gdown.download(f"https://drive.google.com/uc?id={DRIVE_FILE_ID}", archive_path, quiet=False)
        except Exception as e:
            print(f"Download failed: {e}")

if os.path.exists(destination_path):
    if not os.path.exists(extract_path) or len(os.listdir(extract_path)) == 0:
        os.makedirs(extract_path, exist_ok=True)
        print("Extracting dataset...")
        try:
            with zipfile.ZipFile(destination_path, 'r') as zip_ref:
                zip_ref.extractall(path=extract_path)
            print("Extraction completed!")
        except Exception as e:
            print(f"Extraction failed: {e}")
    else:
        print("Dataset ready.")
Dataset ready.

3. Data Cleaning dan Validasi Gambar (RGB)

In [15]:
# --- Determine dataset path ---
# Colab: zip extracts to dataset/ with dataset_jagung_v1/ subfolder
# Local: zip extracts classes directly to dataset/
if IS_COLAB:
    dataset_path = "/content/dataset/dataset_jagung_v1"
else:
    candidate = os.path.join(extract_path, "dataset_jagung_v1")
    if os.path.isdir(candidate):
        dataset_path = candidate
    else:
        dataset_path = extract_path  # classes are directly in dataset/

print(f"Dataset path: {dataset_path}")

def clean_image_data(directory):
    removed_count = 0
    for root, dirs, files in os.walk(directory):
        for file in files:
            file_path = os.path.join(root, file)
            try:
                img = Image.open(file_path)
                img.verify()
                img = Image.open(file_path)
                if img.mode != 'RGB':
                    img = img.convert('RGB')
                    img.save(file_path)
            except Exception:
                print(f"Removing: {file_path}")
                os.remove(file_path)
                removed_count += 1
    return removed_count

print("Cleaning data...")
removed = clean_image_data(dataset_path)
print(f"Done. {removed} problematic files removed.")
Dataset path: /home/asephs/ZeaVis-Edu/Machine_Learning/dataset
Cleaning data...
Done. 0 problematic files removed.

4. Data Splitting (Train:Validation:Test = 70:15:15)

In [16]:
if IS_COLAB:
    output_dir = "/content/dataset_split"
else:
    output_dir = os.path.join(os.getcwd(), "dataset_split")

if os.path.exists(output_dir):
    shutil.rmtree(output_dir)

print("Splitting dataset 70:15:15...")
splitfolders.ratio(dataset_path, output=output_dir,
                   seed=SEED, ratio=(0.7, 0.15, 0.15),
                   group_prefix=None, move=False)
print("Done.")

train_dir = os.path.join(output_dir, 'train')
val_dir = os.path.join(output_dir, 'val')
test_dir = os.path.join(output_dir, 'test')

# Count
def count_images(path):
    return sum(len(files) for _, _, files in os.walk(path))

print(f'Train: {count_images(train_dir)} | Val: {count_images(val_dir)} | Test: {count_images(test_dir)}')
Splitting dataset 70:15:15...
Copying files: 6988 files [00:08, 830.89 files/s] 
Done.
Train: 4890 | Val: 1046 | Test: 1052

5. Data Loader dengan Augmentasi Lanjutan

Menggunakan pipeline augmentasi berlapis:

  • Geometric: RandomFlip, Rotation, Zoom, Translation, Contrast, Brightness
  • CutMix & MixUp: mencampur dua gambar dan label (probabilitas 50% masing-masing)
  • RandomErasing: menghapus area acak pada gambar
  • GaussianNoise di dalam model (layer terpisah)
  • Class weights: menangani ketidakseimbangan kelas (capped max 2.5)
In [17]:
# --- Load datasets ---
train_ds = tf.keras.utils.image_dataset_from_directory(
    train_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE)
val_ds = tf.keras.utils.image_dataset_from_directory(
    val_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE)
test_ds = tf.keras.utils.image_dataset_from_directory(
    test_dir, shuffle=False, batch_size=BATCH_SIZE, image_size=IMG_SIZE)

class_names = train_ds.class_names
NUM_CLASSES = len(class_names)
print(f"Classes ({NUM_CLASSES}): {class_names}")

# --- Helper: one-hot ---
def to_one_hot(image, label):
    return image, tf.one_hot(label, NUM_CLASSES)

# --- Beta distribution for MixUp/CutMix ---
def sample_beta_distribution(size, concentration_0=0.2, concentration_1=0.2):
    gamma_1 = tf.random.gamma(shape=[size], alpha=concentration_1, dtype=tf.float32)
    gamma_2 = tf.random.gamma(shape=[size], alpha=concentration_0, dtype=tf.float32)
    return gamma_2 / (gamma_1 + gamma_2 + 1e-8)

# --- MixUp ---
def mix_up(images, labels, alpha=0.2):
    batch_size = tf.shape(images)[0]
    lambda_param = sample_beta_distribution(batch_size, alpha, alpha)
    lambda_param_img = tf.reshape(lambda_param, [batch_size, 1, 1, 1])
    random_indices = tf.random.shuffle(tf.range(batch_size))
    mixed_images = lambda_param_img * images + (1 - lambda_param_img) * tf.gather(images, random_indices)
    labels = tf.cast(labels, tf.float32)
    lambda_param_lbl = tf.reshape(lambda_param, [-1, 1])
    mixed_labels = lambda_param_lbl * labels + (1 - lambda_param_lbl) * tf.gather(labels, random_indices)
    return mixed_images, mixed_labels

# --- CutMix (batch-level via coordinate-grid broadcasting) ---
def cut_mix(images, labels, alpha=0.2):
    batch_size = tf.shape(images)[0]
    h = tf.shape(images)[1]
    w = tf.shape(images)[2]
    
    lambda_param = sample_beta_distribution(batch_size, alpha, alpha)  # [B]
    random_indices = tf.random.shuffle(tf.range(batch_size))
    
    # Per-sample cut sizes (in pixels)
    cut_ratio = tf.sqrt(1.0 - lambda_param)  # [B]
    r_h = tf.cast(cut_ratio * tf.cast(h, tf.float32), tf.int32)  # [B]
    r_w = tf.cast(cut_ratio * tf.cast(w, tf.float32), tf.int32)  # [B]
    
    # Per-sample random cut centres
    cx = tf.random.uniform([batch_size], 0, w, tf.int32)  # [B]
    cy = tf.random.uniform([batch_size], 0, h, tf.int32)  # [B]
    
    half_h = r_h // 2
    half_w = r_w // 2
    x1 = tf.clip_by_value(cx - half_w, 0, w)  # [B]
    x2 = tf.clip_by_value(cx + half_w, 0, w)  # [B]
    y1 = tf.clip_by_value(cy - half_h, 0, h)  # [B]
    y2 = tf.clip_by_value(cy + half_h, 0, h)  # [B]
    
    # Build boolean cut mask via coordinate broadcasting
    col_idx = tf.range(w, dtype=tf.int32)          # [W]
    row_idx = tf.range(h, dtype=tf.int32)          # [H]
    
    in_x = tf.logical_and(
        tf.reshape(col_idx, [1, 1, w]) >= tf.reshape(x1, [batch_size, 1, 1]),
        tf.reshape(col_idx, [1, 1, w]) <  tf.reshape(x2, [batch_size, 1, 1])
    )                                               # [B, 1, W]
    in_y = tf.logical_and(
        tf.reshape(row_idx, [1, h, 1]) >= tf.reshape(y1, [batch_size, 1, 1]),
        tf.reshape(row_idx, [1, h, 1]) <  tf.reshape(y2, [batch_size, 1, 1])
    )                                               # [B, H, 1]
    
    cut_mask = tf.cast(tf.logical_and(in_y, in_x), tf.float32)  # [B, H, W]
    cut_mask = tf.expand_dims(cut_mask, -1)                     # [B, H, W, 1]
    
    # Mix images
    shuffled = tf.gather(images, random_indices)
    mixed_images = (1.0 - cut_mask) * images + cut_mask * shuffled
    
    # Mix labels
    labels = tf.cast(labels, tf.float32)
    lambda_reshaped = tf.reshape(lambda_param, [-1, 1])
    mixed_labels = lambda_reshaped * labels + (1.0 - lambda_reshaped) * tf.gather(labels, random_indices)
    
    return mixed_images, mixed_labels

# --- RandomErasing ---
def random_erasing(images, probability=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3)):
    batch_size = tf.shape(images)[0]
    h = tf.shape(images)[1]
    w = tf.shape(images)[2]
    
    target_area = tf.random.uniform([], scale[0], scale[1]) * tf.cast(h * w, tf.float32)
    aspect_ratio = tf.random.uniform([], ratio[0], ratio[1])
    erasing_h = tf.cast(tf.math.sqrt(target_area / aspect_ratio), tf.int32)
    erasing_w = tf.cast(tf.math.sqrt(target_area * aspect_ratio), tf.int32)
    erasing_h = tf.clip_by_value(erasing_h, 1, h - 1)
    erasing_w = tf.clip_by_value(erasing_w, 1, w - 1)
    
    cx = tf.random.uniform([], 0, w - erasing_w, tf.int32)
    cy = tf.random.uniform([], 0, h - erasing_h, tf.int32)
    
    # Build erase mask via coordinate broadcasting (same pattern as CutMix)
    col_idx = tf.range(w, dtype=tf.int32)
    row_idx = tf.range(h, dtype=tf.int32)
    
    in_x = tf.logical_and(col_idx >= cx, col_idx < cx + erasing_w)        # [W]
    in_y = tf.logical_and(row_idx >= cy, row_idx < cy + erasing_h)        # [H]
    erase_mask = tf.cast(tf.expand_dims(in_y, 1) & tf.expand_dims(in_x, 0), tf.float32)  # [H, W]
    erase_mask = tf.expand_dims(erase_mask, 0)   # [1, H, W]
    erase_mask = tf.expand_dims(erase_mask, -1)  # [1, H, W, 1]
    
    noise = tf.random.uniform([batch_size, erasing_h, erasing_w, 3], 0.0, 255.0, dtype=tf.float32)
    
    # Pad the noise patch to full image size
    paddings = [[0, 0], [cy, h - (cy + erasing_h)], [cx, w - (cx + erasing_w)], [0, 0]]
    noise_padded = tf.pad(noise, paddings, constant_values=0.0)
    
    erased = images * (1.0 - erase_mask) + noise_padded * erase_mask
    return tf.cond(
        tf.random.uniform([]) < probability,
        lambda: erased,
        lambda: images
    )

# --- Geometric augmentation (kept for TTA compatibility) ---
data_augmentation = tf.keras.Sequential([
    layers.RandomFlip("horizontal_and_vertical"),
    layers.RandomRotation(0.2),
    layers.RandomZoom(0.2),
    layers.RandomTranslation(0.1, 0.1),
    layers.RandomContrast(0.2),
    layers.RandomBrightness(0.2)
], name="data_augmentation")

# --- Augment + MixUp/CutMix + RandomErasing pipeline ---
def augment_and_mix(images, labels):
    # images are uint8 [0,255], labels are int
    # 1. Geometric augmentation
    images = data_augmentation(images, training=True)
    # 2. Convert to float32 for MixUp/CutMix
    images = tf.cast(images, tf.float32)
    # 3. Apply CutMix or MixUp with 50% probability
    choice = tf.random.uniform([])
    labels_onehot = tf.one_hot(labels, NUM_CLASSES)
    images, labels_onehot = tf.cond(
        choice < 0.3,  # 30% MixUp
        lambda: mix_up(images, labels_onehot),
        lambda: tf.cond(
            choice < 0.6,  # 30% CutMix
            lambda: cut_mix(images, labels_onehot),
            lambda: (images, labels_onehot)  # 40% no mix
        )
    )
    # 4. Random Erasing
    images = random_erasing(images, probability=0.25)
    return images, labels_onehot

# --- Preprocessing (EfficientNetV2: [-1,1]) ---
def preprocess_fn(image, label):
    return preprocess_input(image), label

# --- Class Weights ---
train_class_counts = Counter()
for cn in class_names:
    p = os.path.join(train_dir, cn)
    if os.path.exists(p):
        train_class_counts[cn] = len(os.listdir(p))

y_integer = []
for i, cn in enumerate(class_names):
    y_integer.extend([i] * train_class_counts[cn])

class_weights_array = compute_class_weight('balanced', classes=np.unique(y_integer), y=y_integer)
class_weights_capped = [min(w, 2.5) for w in class_weights_array]
class_weights_tensor = tf.constant(class_weights_capped, dtype=tf.float32)

def add_sample_weight(image, label):
    class_indices = tf.argmax(label, axis=-1)
    sample_weights = tf.gather(class_weights_tensor, class_indices)
    return image, label, sample_weights

print("Class weights (capped at 2.5):")
for i, cn in enumerate(class_names):
    print(f"  {cn}: {class_weights_capped[i]:.4f}")

# --- Build pipelines ---
# Train: augment → mix → erase → preprocess → onehot → sample_weight → prefetch
train_ds = (train_ds
    .map(augment_and_mix, num_parallel_calls=AUTOTUNE)
    .map(preprocess_fn, num_parallel_calls=AUTOTUNE)
    .map(add_sample_weight, num_parallel_calls=AUTOTUNE)
    .prefetch(AUTOTUNE))

# Val/Test: preprocess → onehot → prefetch
val_ds = (val_ds
    .map(lambda img, lbl: (preprocess_input(tf.cast(img, tf.float32)), lbl), num_parallel_calls=AUTOTUNE)
    .map(to_one_hot, num_parallel_calls=AUTOTUNE)
    .prefetch(AUTOTUNE))

test_ds = (test_ds
    .map(lambda img, lbl: (preprocess_input(tf.cast(img, tf.float32)), lbl), num_parallel_calls=AUTOTUNE)
    .map(to_one_hot, num_parallel_calls=AUTOTUNE)
    .prefetch(AUTOTUNE))

print("Data pipelines ready.")
Found 4890 files belonging to 4 classes.
Found 1046 files belonging to 4 classes.
Found 1052 files belonging to 4 classes.
Classes (4): ['Bercak Daun', 'Daun Sehat', 'Hawar Daun', 'Karat Daun']
Class weights (capped at 2.5):
  Bercak Daun: 1.1665
  Daun Sehat: 0.8080
  Hawar Daun: 1.0847
  Karat Daun: 1.0171
Data pipelines ready.

6. Visualisasi Sampel Data (dengan Augmentasi)

In [18]:
# Visualisasi augmentasi (non-preprocessed)
vis_ds = tf.keras.utils.image_dataset_from_directory(
    train_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE)

plt.figure(figsize=(16, 10))
for images, labels in vis_ds.take(1):
    # Original (4)
    for i in range(4):
        plt.subplot(3, 4, i + 1)
        plt.imshow(images[i].numpy().astype("uint8"))
        plt.title(f"Asli: {class_names[labels[i].numpy()]}", fontsize=11)
        plt.axis("off")
    # Augmented (4) — geometric only
    aug = data_augmentation(images, training=True)
    for i in range(4):
        plt.subplot(3, 4, i + 5)
        plt.imshow(aug[i].numpy().astype("uint8"))
        plt.title(f"Aug: {class_names[labels[i].numpy()]}", fontsize=11)
        plt.axis("off")
    # MixUp/CutMix result (4)
    aug_f = tf.cast(images, tf.float32)
    mixed, _ = mix_up(aug_f, tf.one_hot(labels, NUM_CLASSES))
    for i in range(4):
        plt.subplot(3, 4, i + 9)
        plt.imshow(tf.clip_by_value(mixed[i], 0, 255).numpy().astype("uint8"))
        plt.title("MixUp/CutMix", fontsize=11)
        plt.axis("off")

plt.suptitle("Contoh Augmentasi Berlapis", fontsize=16)
plt.tight_layout()
plt.show()
Found 4890 files belonging to 4 classes.
2026-06-11 17:05:59.854775: I tensorflow/core/framework/local_rendezvous.cc:407] Local rendezvous is aborting with status: OUT_OF_RANGE: End of sequence

7. Arsitektur Model CNN (EfficientNetV2B0)

Base model EfficientNetV2B0 (imagenet, frozen) + Conv2D tambahan + BatchNorm + Dropout + Dense. Ditambah GaussianNoise(0.1) untuk regularisasi tambahan.

In [19]:
def build_model(num_classes):
    base_model = EfficientNetV2B0(
        input_shape=IMG_SIZE + (3,),
        include_top=False,
        weights='imagenet',
    )
    base_model.trainable = False

    inputs = tf.keras.Input(shape=IMG_SIZE + (3,))
    x = layers.GaussianNoise(0.1)(inputs)
    x = base_model(x, training=False)
    x = layers.Conv2D(512, (3, 3), padding='same', activation='swish')(x)
    x = layers.BatchNormalization()(x)
    x = layers.MaxPooling2D((2, 2))(x)
    x = layers.Dropout(0.2)(x)
    x = layers.Conv2D(256, (3, 3), padding='same', activation='swish')(x)
    x = layers.BatchNormalization()(x)
    x = layers.GlobalAveragePooling2D()(x)
    x = layers.Dropout(0.3)(x)
    x = layers.Dense(1024, activation='swish')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Dropout(0.4)(x)
    outputs = layers.Dense(num_classes, activation='softmax', dtype='float32')(x)
    return models.Model(inputs, outputs), base_model

if IS_COLAB:
    ckpt_dir = '/content/best_model'
else:
    ckpt_dir = os.path.join(os.getcwd(), 'best_model')
checkpoint_path = os.path.join(ckpt_dir, 'best_model.keras')

if os.path.exists(checkpoint_path):
    print(f"Loading checkpoint: {checkpoint_path}")
    try:
        model = models.load_model(checkpoint_path, compile=False)
    except Exception as e:
        print(f"Load failed: {e}. Building fresh.")
        model, base_model = build_model(NUM_CLASSES)
else:
    print("No checkpoint. Building fresh model.")
    model, base_model = build_model(NUM_CLASSES)

os.makedirs(ckpt_dir, exist_ok=True)
model.summary()
Loading checkpoint: /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
Model: "functional_1"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                     Output Shape                  Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ input_layer_2 (InputLayer)      │ (None, 224, 224, 3)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ gaussian_noise (GaussianNoise)  │ (None, 224, 224, 3)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ efficientnetv2-b0 (Functional)  │ (None, 7, 7, 1280)     │     5,919,312 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d (Conv2D)                 │ (None, 7, 7, 512)      │     5,898,752 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization             │ (None, 7, 7, 512)      │         2,048 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d (MaxPooling2D)    │ (None, 3, 3, 512)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout (Dropout)               │ (None, 3, 3, 512)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_1 (Conv2D)               │ (None, 3, 3, 256)      │     1,179,904 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_1           │ (None, 3, 3, 256)      │         1,024 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ global_average_pooling2d        │ (None, 256)            │             0 │
│ (GlobalAveragePooling2D)        │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_1 (Dropout)             │ (None, 256)            │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense)                   │ (None, 1024)           │       263,168 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_2           │ (None, 1024)           │         4,096 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_2 (Dropout)             │ (None, 1024)           │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 4)              │         4,100 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 13,272,404 (50.63 MB)
 Trainable params: 7,349,508 (28.04 MB)
 Non-trainable params: 5,922,896 (22.59 MB)

8. Callbacks

  • ModelCheckpoint: simpan yang terbaik (val_accuracy)
  • EarlyStopping: patience 15 (lebih panjang untuk fine-tuning)
  • ReduceLROnPlateau: faktor 0.2, patience 5, min 1e-8
  • CSVLogger: riwayat training
In [20]:
import time

log_dir = "logs/fit/" + time.strftime("%Y%m%d-%H%M%S")

checkpoint_cb = callbacks.ModelCheckpoint(
    checkpoint_path,
    save_best_only=True,
    monitor="val_accuracy",
    mode="max",
    verbose=1
)

early_stopping_cb = callbacks.EarlyStopping(
    monitor="val_accuracy",
    patience=15,
    restore_best_weights=True,
    mode="max",
    verbose=1
)

reduce_lr_cb = callbacks.ReduceLROnPlateau(
    monitor='val_loss',
    factor=0.2,
    patience=5,
    min_lr=1e-8,
    verbose=1,
    mode="min"
)

csv_logger = callbacks.CSVLogger(os.path.join(ckpt_dir, 'training_log.csv'))

callbacks_list = [checkpoint_cb, early_stopping_cb, reduce_lr_cb, csv_logger]

9. Pelatihan Fase 1 — Head Only

Optimizer AdamW dengan EMA (Exponential Moving Average) dan Label Smoothing 0.2. Base model beku, hanya head yang dilatih.

In [21]:
model.compile(
    optimizer=AdamW(use_ema=True, ema_momentum=0.999,
                    learning_rate=1e-3, weight_decay=1e-4),
    loss=tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.2),
    metrics=['accuracy']
)

print("Fase 1: Head training (base frozen)...")
history_1 = model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=30,
    callbacks=callbacks_list
)
Fase 1: Head training (base frozen)...
Epoch 1/30
Corrupt JPEG data: bad Huffman code
 72/153 ━━━━━━━━━━━━━━━━━━━━ 2s 36ms/step - accuracy: 0.8908 - loss: 0.8013
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 135ms/step - accuracy: 0.8922 - loss: 0.8094
Epoch 1: val_accuracy improved from None to 0.95411, saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras

Epoch 1: finished saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
153/153 ━━━━━━━━━━━━━━━━━━━━ 61s 215ms/step - accuracy: 0.8941 - loss: 0.8123 - val_accuracy: 0.9541 - val_loss: 0.7203 - learning_rate: 0.0010
Epoch 2/30
 33/153 ━━━━━━━━━━━━━━━━━━━━ 8s 68ms/step - accuracy: 0.9128 - loss: 0.7974
Corrupt JPEG data: bad Huffman code
 64/153 ━━━━━━━━━━━━━━━━━━━━ 6s 72ms/step - accuracy: 0.9082 - loss: 0.8043
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 69ms/step - accuracy: 0.9014 - loss: 0.8124
Epoch 2: val_accuracy improved from 0.95411 to 0.95985, saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras

Epoch 2: finished saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
153/153 ━━━━━━━━━━━━━━━━━━━━ 15s 94ms/step - accuracy: 0.8957 - loss: 0.8151 - val_accuracy: 0.9598 - val_loss: 0.7770 - learning_rate: 0.0010
Epoch 3/30
 31/153 ━━━━━━━━━━━━━━━━━━━━ 8s 68ms/step - accuracy: 0.8883 - loss: 0.8339
Corrupt JPEG data: bad Huffman code
 68/153 ━━━━━━━━━━━━━━━━━━━━ 5s 68ms/step - accuracy: 0.8923 - loss: 0.8253
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 68ms/step - accuracy: 0.8969 - loss: 0.8170
Epoch 3: val_accuracy improved from 0.95985 to 0.97514, saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras

Epoch 3: finished saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
153/153 ━━━━━━━━━━━━━━━━━━━━ 16s 96ms/step - accuracy: 0.9033 - loss: 0.8026 - val_accuracy: 0.9751 - val_loss: 0.6700 - learning_rate: 0.0010
Epoch 4/30
 38/153 ━━━━━━━━━━━━━━━━━━━━ 9s 78ms/step - accuracy: 0.8932 - loss: 0.8082
Corrupt JPEG data: bad Huffman code
 72/153 ━━━━━━━━━━━━━━━━━━━━ 5s 72ms/step - accuracy: 0.8967 - loss: 0.8057
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.8977 - loss: 0.8050
Epoch 4: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 13s 84ms/step - accuracy: 0.8984 - loss: 0.8053 - val_accuracy: 0.9618 - val_loss: 0.6673 - learning_rate: 0.0010
Epoch 5/30
 31/153 ━━━━━━━━━━━━━━━━━━━━ 9s 82ms/step - accuracy: 0.8997 - loss: 0.8094 
Corrupt JPEG data: bad Huffman code
 87/153 ━━━━━━━━━━━━━━━━━━━━ 4s 71ms/step - accuracy: 0.8984 - loss: 0.8092
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 68ms/step - accuracy: 0.9004 - loss: 0.8062
Epoch 5: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 13s 82ms/step - accuracy: 0.9055 - loss: 0.7987 - val_accuracy: 0.9512 - val_loss: 0.7019 - learning_rate: 0.0010
Epoch 6/30
 40/153 ━━━━━━━━━━━━━━━━━━━━ 8s 77ms/step - accuracy: 0.9069 - loss: 0.7904
Corrupt JPEG data: bad Huffman code
 73/153 ━━━━━━━━━━━━━━━━━━━━ 5s 71ms/step - accuracy: 0.9013 - loss: 0.8002
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.8983 - loss: 0.8076
Epoch 6: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 13s 82ms/step - accuracy: 0.8965 - loss: 0.8128 - val_accuracy: 0.9646 - val_loss: 0.6677 - learning_rate: 0.0010
Epoch 7/30
 34/153 ━━━━━━━━━━━━━━━━━━━━ 9s 82ms/step - accuracy: 0.9011 - loss: 0.7930 
Corrupt JPEG data: bad Huffman code
 76/153 ━━━━━━━━━━━━━━━━━━━━ 5s 75ms/step - accuracy: 0.8995 - loss: 0.8002
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9031 - loss: 0.7991
Epoch 7: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 13s 84ms/step - accuracy: 0.9065 - loss: 0.7975 - val_accuracy: 0.9685 - val_loss: 0.6607 - learning_rate: 0.0010
Epoch 8/30
 33/153 ━━━━━━━━━━━━━━━━━━━━ 8s 74ms/step - accuracy: 0.9106 - loss: 0.7855
Corrupt JPEG data: bad Huffman code
 69/153 ━━━━━━━━━━━━━━━━━━━━ 5s 71ms/step - accuracy: 0.9116 - loss: 0.7886
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 69ms/step - accuracy: 0.9099 - loss: 0.7915
Epoch 8: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 84ms/step - accuracy: 0.9108 - loss: 0.7886 - val_accuracy: 0.9732 - val_loss: 0.6645 - learning_rate: 0.0010
Epoch 9/30
 38/153 ━━━━━━━━━━━━━━━━━━━━ 7s 64ms/step - accuracy: 0.9102 - loss: 0.7870
Corrupt JPEG data: bad Huffman code
 74/153 ━━━━━━━━━━━━━━━━━━━━ 5s 75ms/step - accuracy: 0.9106 - loss: 0.7890
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9121 - loss: 0.7885
Epoch 9: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9119 - loss: 0.7855 - val_accuracy: 0.9474 - val_loss: 0.6715 - learning_rate: 0.0010
Epoch 10/30
 41/153 ━━━━━━━━━━━━━━━━━━━━ 8s 72ms/step - accuracy: 0.9041 - loss: 0.7846
Corrupt JPEG data: bad Huffman code
 84/153 ━━━━━━━━━━━━━━━━━━━━ 4s 70ms/step - accuracy: 0.9028 - loss: 0.7952
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 69ms/step - accuracy: 0.9047 - loss: 0.7963
Epoch 10: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 82ms/step - accuracy: 0.9102 - loss: 0.7911 - val_accuracy: 0.9656 - val_loss: 0.6569 - learning_rate: 0.0010
Epoch 11/30
 34/153 ━━━━━━━━━━━━━━━━━━━━ 7s 66ms/step - accuracy: 0.9093 - loss: 0.7968
Corrupt JPEG data: bad Huffman code
 73/153 ━━━━━━━━━━━━━━━━━━━━ 5s 69ms/step - accuracy: 0.9116 - loss: 0.7899
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 68ms/step - accuracy: 0.9124 - loss: 0.7892
Epoch 11: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 20s 81ms/step - accuracy: 0.9110 - loss: 0.7865 - val_accuracy: 0.9522 - val_loss: 0.6961 - learning_rate: 0.0010
Epoch 12/30
 43/153 ━━━━━━━━━━━━━━━━━━━━ 7s 66ms/step - accuracy: 0.9099 - loss: 0.7873
Corrupt JPEG data: bad Huffman code
 82/153 ━━━━━━━━━━━━━━━━━━━━ 4s 68ms/step - accuracy: 0.9096 - loss: 0.7893
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 67ms/step - accuracy: 0.9108 - loss: 0.7897
Epoch 12: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 13s 80ms/step - accuracy: 0.9147 - loss: 0.7860 - val_accuracy: 0.9627 - val_loss: 0.6555 - learning_rate: 0.0010
Epoch 13/30
 38/153 ━━━━━━━━━━━━━━━━━━━━ 6s 60ms/step - accuracy: 0.8981 - loss: 0.7914
Corrupt JPEG data: bad Huffman code
 66/153 ━━━━━━━━━━━━━━━━━━━━ 5s 68ms/step - accuracy: 0.9016 - loss: 0.7904
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 66ms/step - accuracy: 0.9061 - loss: 0.7873
Epoch 13: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 82ms/step - accuracy: 0.9129 - loss: 0.7795 - val_accuracy: 0.9560 - val_loss: 0.6718 - learning_rate: 0.0010
Epoch 14/30
 56/153 ━━━━━━━━━━━━━━━━━━━━ 7s 75ms/step - accuracy: 0.9193 - loss: 0.7872
Corrupt JPEG data: bad Huffman code
 70/153 ━━━━━━━━━━━━━━━━━━━━ 6s 77ms/step - accuracy: 0.9188 - loss: 0.7865
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 74ms/step - accuracy: 0.9196 - loss: 0.7812
Epoch 14: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 15s 89ms/step - accuracy: 0.9180 - loss: 0.7753 - val_accuracy: 0.9570 - val_loss: 0.6654 - learning_rate: 0.0010
Epoch 15/30
 35/153 ━━━━━━━━━━━━━━━━━━━━ 8s 74ms/step - accuracy: 0.9333 - loss: 0.7673
Corrupt JPEG data: bad Huffman code
 68/153 ━━━━━━━━━━━━━━━━━━━━ 5s 69ms/step - accuracy: 0.9285 - loss: 0.7714
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 69ms/step - accuracy: 0.9224 - loss: 0.7744
Epoch 15: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 83ms/step - accuracy: 0.9172 - loss: 0.7775 - val_accuracy: 0.9589 - val_loss: 0.6675 - learning_rate: 0.0010
Epoch 16/30
 29/153 ━━━━━━━━━━━━━━━━━━━━ 9s 73ms/step - accuracy: 0.9020 - loss: 0.7942
Corrupt JPEG data: bad Huffman code
 68/153 ━━━━━━━━━━━━━━━━━━━━ 6s 72ms/step - accuracy: 0.9073 - loss: 0.7869
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.9098 - loss: 0.7838
Epoch 16: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 87ms/step - accuracy: 0.9135 - loss: 0.7756 - val_accuracy: 0.9656 - val_loss: 0.6626 - learning_rate: 0.0010
Epoch 17/30
 36/153 ━━━━━━━━━━━━━━━━━━━━ 9s 78ms/step - accuracy: 0.9200 - loss: 0.7642
Corrupt JPEG data: bad Huffman code
 67/153 ━━━━━━━━━━━━━━━━━━━━ 6s 78ms/step - accuracy: 0.9167 - loss: 0.7716
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 73ms/step - accuracy: 0.9165 - loss: 0.7747
Epoch 17: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9160 - loss: 0.7748 - val_accuracy: 0.9723 - val_loss: 0.6553 - learning_rate: 0.0010
Epoch 18/30
 31/153 ━━━━━━━━━━━━━━━━━━━━ 9s 77ms/step - accuracy: 0.9418 - loss: 0.7480 
Corrupt JPEG data: bad Huffman code
 70/153 ━━━━━━━━━━━━━━━━━━━━ 6s 78ms/step - accuracy: 0.9343 - loss: 0.7633
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9280 - loss: 0.7724
Epoch 18: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 86ms/step - accuracy: 0.9223 - loss: 0.7801 - val_accuracy: 0.9713 - val_loss: 0.6571 - learning_rate: 0.0010
Epoch 18: early stopping
Restoring model weights from the end of the best epoch: 3.

10. Fine-tuning Fase 2 — Unfreeze Layer Atas

Membuka 100 layer teratas base model. Learning rate diturunkan ke 1e-4.

In [22]:
base_model = model.layers[2]  # layer[0]=InputLayer, layer[1]=GaussianNoise, layer[2]=EfficientNetV2B0
base_model.trainable = True
for layer in base_model.layers[:-100]:
    layer.trainable = False

model.compile(
    optimizer=AdamW(use_ema=True, ema_momentum=0.999,
                    learning_rate=1e-4, weight_decay=1e-4),
    loss=tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.2),
    metrics=['accuracy']
)

print("Fase 2: Fine-tuning top 100 layers...")
history_2 = model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=60,
    initial_epoch=history_1.epoch[-1] + 1,
    callbacks=callbacks_list
)
Fase 2: Fine-tuning top 100 layers...
Epoch 19/60
Corrupt JPEG data: bad Huffman code
2026-06-11 17:11:41.079500: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:11:41.237435: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
 78/153 ━━━━━━━━━━━━━━━━━━━━ 2s 39ms/step - accuracy: 0.8603 - loss: 0.8519
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
150/153 ━━━━━━━━━━━━━━━━━━━━ 0s 51ms/step - accuracy: 0.8652 - loss: 0.8433
2026-06-11 17:12:10.589920: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:12:10.748299: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 194ms/step - accuracy: 0.8654 - loss: 0.8430
Epoch 19: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 89s 259ms/step - accuracy: 0.8763 - loss: 0.8239 - val_accuracy: 0.9637 - val_loss: 0.6448 - learning_rate: 1.0000e-04
Epoch 20/60
 33/153 ━━━━━━━━━━━━━━━━━━━━ 7s 67ms/step - accuracy: 0.8962 - loss: 0.8058
Corrupt JPEG data: bad Huffman code
 70/153 ━━━━━━━━━━━━━━━━━━━━ 5s 70ms/step - accuracy: 0.9015 - loss: 0.8021
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 67ms/step - accuracy: 0.9021 - loss: 0.7997
Epoch 20: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 82ms/step - accuracy: 0.9033 - loss: 0.7931 - val_accuracy: 0.9656 - val_loss: 0.6464 - learning_rate: 1.0000e-04
Epoch 21/60
 38/153 ━━━━━━━━━━━━━━━━━━━━ 9s 80ms/step - accuracy: 0.9096 - loss: 0.7774
Corrupt JPEG data: bad Huffman code
 68/153 ━━━━━━━━━━━━━━━━━━━━ 6s 75ms/step - accuracy: 0.9055 - loss: 0.7842
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9021 - loss: 0.7901
Epoch 21: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9027 - loss: 0.7900 - val_accuracy: 0.9637 - val_loss: 0.6389 - learning_rate: 1.0000e-04
Epoch 22/60
 41/153 ━━━━━━━━━━━━━━━━━━━━ 9s 86ms/step - accuracy: 0.9098 - loss: 0.7861 
Corrupt JPEG data: bad Huffman code
 73/153 ━━━━━━━━━━━━━━━━━━━━ 6s 77ms/step - accuracy: 0.9108 - loss: 0.7885
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 74ms/step - accuracy: 0.9111 - loss: 0.7886
Epoch 22: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 89ms/step - accuracy: 0.9096 - loss: 0.7858 - val_accuracy: 0.9751 - val_loss: 0.6314 - learning_rate: 1.0000e-04
Epoch 23/60
 46/153 ━━━━━━━━━━━━━━━━━━━━ 8s 77ms/step - accuracy: 0.9281 - loss: 0.7582
Corrupt JPEG data: bad Huffman code
 72/153 ━━━━━━━━━━━━━━━━━━━━ 6s 77ms/step - accuracy: 0.9233 - loss: 0.7659
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 73ms/step - accuracy: 0.9193 - loss: 0.7723
Epoch 23: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 88ms/step - accuracy: 0.9176 - loss: 0.7735 - val_accuracy: 0.9751 - val_loss: 0.6300 - learning_rate: 1.0000e-04
Epoch 24/60
 30/153 ━━━━━━━━━━━━━━━━━━━━ 9s 81ms/step - accuracy: 0.9006 - loss: 0.7649 
Corrupt JPEG data: bad Huffman code
 76/153 ━━━━━━━━━━━━━━━━━━━━ 5s 72ms/step - accuracy: 0.9058 - loss: 0.7704
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9101 - loss: 0.7699
Epoch 24: val_accuracy did not improve from 0.97514
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9172 - loss: 0.7660 - val_accuracy: 0.9723 - val_loss: 0.6350 - learning_rate: 1.0000e-04
Epoch 25/60
 50/153 ━━━━━━━━━━━━━━━━━━━━ 7s 77ms/step - accuracy: 0.9242 - loss: 0.7608
Corrupt JPEG data: bad Huffman code
 72/153 ━━━━━━━━━━━━━━━━━━━━ 6s 76ms/step - accuracy: 0.9237 - loss: 0.7632
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9236 - loss: 0.7635
Epoch 25: val_accuracy improved from 0.97514 to 0.97897, saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras

Epoch 25: finished saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
153/153 ━━━━━━━━━━━━━━━━━━━━ 16s 98ms/step - accuracy: 0.9239 - loss: 0.7602 - val_accuracy: 0.9790 - val_loss: 0.6311 - learning_rate: 1.0000e-04
Epoch 26/60
 38/153 ━━━━━━━━━━━━━━━━━━━━ 7s 67ms/step - accuracy: 0.9397 - loss: 0.7498
Corrupt JPEG data: bad Huffman code
 68/153 ━━━━━━━━━━━━━━━━━━━━ 5s 70ms/step - accuracy: 0.9341 - loss: 0.7563
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9308 - loss: 0.7591
Epoch 26: val_accuracy did not improve from 0.97897
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9284 - loss: 0.7577 - val_accuracy: 0.9780 - val_loss: 0.6284 - learning_rate: 1.0000e-04
Epoch 27/60
 30/153 ━━━━━━━━━━━━━━━━━━━━ 10s 83ms/step - accuracy: 0.9301 - loss: 0.7455
Corrupt JPEG data: bad Huffman code
 84/153 ━━━━━━━━━━━━━━━━━━━━ 5s 76ms/step - accuracy: 0.9292 - loss: 0.7529
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 73ms/step - accuracy: 0.9306 - loss: 0.7538
Epoch 27: val_accuracy did not improve from 0.97897
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 87ms/step - accuracy: 0.9339 - loss: 0.7496 - val_accuracy: 0.9780 - val_loss: 0.6246 - learning_rate: 1.0000e-04
Epoch 28/60
 64/153 ━━━━━━━━━━━━━━━━━━━━ 6s 74ms/step - accuracy: 0.9320 - loss: 0.7515
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
Corrupt JPEG data: bad Huffman code
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 69ms/step - accuracy: 0.9291 - loss: 0.7553
Epoch 28: val_accuracy did not improve from 0.97897
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 86ms/step - accuracy: 0.9278 - loss: 0.7560 - val_accuracy: 0.9771 - val_loss: 0.6247 - learning_rate: 1.0000e-04
Epoch 29/60
 33/153 ━━━━━━━━━━━━━━━━━━━━ 10s 83ms/step - accuracy: 0.9392 - loss: 0.7419
Corrupt JPEG data: bad Huffman code
 65/153 ━━━━━━━━━━━━━━━━━━━━ 6s 79ms/step - accuracy: 0.9357 - loss: 0.7439
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 73ms/step - accuracy: 0.9338 - loss: 0.7478
Epoch 29: val_accuracy improved from 0.97897 to 0.98088, saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras

Epoch 29: finished saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
153/153 ━━━━━━━━━━━━━━━━━━━━ 15s 95ms/step - accuracy: 0.9321 - loss: 0.7475 - val_accuracy: 0.9809 - val_loss: 0.6241 - learning_rate: 1.0000e-04
Epoch 30/60
 32/153 ━━━━━━━━━━━━━━━━━━━━ 8s 69ms/step - accuracy: 0.9211 - loss: 0.7697
Corrupt JPEG data: bad Huffman code
 66/153 ━━━━━━━━━━━━━━━━━━━━ 6s 72ms/step - accuracy: 0.9290 - loss: 0.7616
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 68ms/step - accuracy: 0.9309 - loss: 0.7562
Epoch 30: val_accuracy improved from 0.98088 to 0.98279, saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras

Epoch 30: finished saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
153/153 ━━━━━━━━━━━━━━━━━━━━ 15s 90ms/step - accuracy: 0.9319 - loss: 0.7498 - val_accuracy: 0.9828 - val_loss: 0.6244 - learning_rate: 1.0000e-04
Epoch 31/60
 33/153 ━━━━━━━━━━━━━━━━━━━━ 10s 85ms/step - accuracy: 0.9480 - loss: 0.7287
Corrupt JPEG data: bad Huffman code
 73/153 ━━━━━━━━━━━━━━━━━━━━ 6s 77ms/step - accuracy: 0.9436 - loss: 0.7344
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.9419 - loss: 0.7386
Epoch 31: val_accuracy did not improve from 0.98279
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9393 - loss: 0.7419 - val_accuracy: 0.9828 - val_loss: 0.6205 - learning_rate: 1.0000e-04
Epoch 32/60
 34/153 ━━━━━━━━━━━━━━━━━━━━ 8s 75ms/step - accuracy: 0.9310 - loss: 0.7688
Corrupt JPEG data: bad Huffman code
 73/153 ━━━━━━━━━━━━━━━━━━━━ 5s 74ms/step - accuracy: 0.9300 - loss: 0.7629
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9298 - loss: 0.7579
Epoch 32: val_accuracy did not improve from 0.98279
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9313 - loss: 0.7486 - val_accuracy: 0.9828 - val_loss: 0.6224 - learning_rate: 1.0000e-04
Epoch 33/60
 33/153 ━━━━━━━━━━━━━━━━━━━━ 8s 71ms/step - accuracy: 0.9487 - loss: 0.7287
Corrupt JPEG data: bad Huffman code
 69/153 ━━━━━━━━━━━━━━━━━━━━ 6s 76ms/step - accuracy: 0.9475 - loss: 0.7323
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 73ms/step - accuracy: 0.9430 - loss: 0.7388
Epoch 33: val_accuracy did not improve from 0.98279
153/153 ━━━━━━━━━━━━━━━━━━━━ 15s 90ms/step - accuracy: 0.9374 - loss: 0.7429 - val_accuracy: 0.9809 - val_loss: 0.6203 - learning_rate: 1.0000e-04
Epoch 34/60
 36/153 ━━━━━━━━━━━━━━━━━━━━ 9s 80ms/step - accuracy: 0.9398 - loss: 0.7303
Corrupt JPEG data: bad Huffman code
 70/153 ━━━━━━━━━━━━━━━━━━━━ 6s 75ms/step - accuracy: 0.9384 - loss: 0.7403
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.9367 - loss: 0.7442
Epoch 34: val_accuracy did not improve from 0.98279
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9374 - loss: 0.7437 - val_accuracy: 0.9790 - val_loss: 0.6263 - learning_rate: 1.0000e-04
Epoch 35/60
 33/153 ━━━━━━━━━━━━━━━━━━━━ 9s 79ms/step - accuracy: 0.9413 - loss: 0.7367
Corrupt JPEG data: bad Huffman code
 73/153 ━━━━━━━━━━━━━━━━━━━━ 6s 76ms/step - accuracy: 0.9405 - loss: 0.7398
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 74ms/step - accuracy: 0.9404 - loss: 0.7415
Epoch 35: val_accuracy did not improve from 0.98279
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 89ms/step - accuracy: 0.9395 - loss: 0.7400 - val_accuracy: 0.9828 - val_loss: 0.6210 - learning_rate: 1.0000e-04
Epoch 36/60
 31/153 ━━━━━━━━━━━━━━━━━━━━ 9s 77ms/step - accuracy: 0.9411 - loss: 0.7312
Corrupt JPEG data: bad Huffman code
 65/153 ━━━━━━━━━━━━━━━━━━━━ 6s 76ms/step - accuracy: 0.9378 - loss: 0.7412
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.9379 - loss: 0.7430
Epoch 36: val_accuracy improved from 0.98279 to 0.98566, saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras

Epoch 36: finished saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
153/153 ━━━━━━━━━━━━━━━━━━━━ 15s 93ms/step - accuracy: 0.9378 - loss: 0.7419 - val_accuracy: 0.9857 - val_loss: 0.6177 - learning_rate: 1.0000e-04
Epoch 37/60
 39/153 ━━━━━━━━━━━━━━━━━━━━ 8s 72ms/step - accuracy: 0.9443 - loss: 0.7402
Corrupt JPEG data: bad Huffman code
 69/153 ━━━━━━━━━━━━━━━━━━━━ 5s 71ms/step - accuracy: 0.9412 - loss: 0.7417
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 68ms/step - accuracy: 0.9393 - loss: 0.7431
Epoch 37: val_accuracy did not improve from 0.98566
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 82ms/step - accuracy: 0.9389 - loss: 0.7403 - val_accuracy: 0.9847 - val_loss: 0.6154 - learning_rate: 1.0000e-04
Epoch 38/60
 37/153 ━━━━━━━━━━━━━━━━━━━━ 9s 82ms/step - accuracy: 0.9461 - loss: 0.7304
Corrupt JPEG data: bad Huffman code
117/153 ━━━━━━━━━━━━━━━━━━━━ 2s 73ms/step - accuracy: 0.9463 - loss: 0.7330
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9453 - loss: 0.7337
Epoch 38: val_accuracy improved from 0.98566 to 0.98662, saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras

Epoch 38: finished saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
153/153 ━━━━━━━━━━━━━━━━━━━━ 15s 96ms/step - accuracy: 0.9417 - loss: 0.7345 - val_accuracy: 0.9866 - val_loss: 0.6151 - learning_rate: 1.0000e-04
Epoch 39/60
 42/153 ━━━━━━━━━━━━━━━━━━━━ 8s 79ms/step - accuracy: 0.9449 - loss: 0.7251
Corrupt JPEG data: bad Huffman code
 71/153 ━━━━━━━━━━━━━━━━━━━━ 6s 79ms/step - accuracy: 0.9439 - loss: 0.7302
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 73ms/step - accuracy: 0.9420 - loss: 0.7343
Epoch 39: val_accuracy did not improve from 0.98662
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 87ms/step - accuracy: 0.9397 - loss: 0.7370 - val_accuracy: 0.9818 - val_loss: 0.6191 - learning_rate: 1.0000e-04
Epoch 40/60
 34/153 ━━━━━━━━━━━━━━━━━━━━ 9s 79ms/step - accuracy: 0.9374 - loss: 0.7308
Corrupt JPEG data: bad Huffman code
 69/153 ━━━━━━━━━━━━━━━━━━━━ 6s 75ms/step - accuracy: 0.9419 - loss: 0.7314
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9420 - loss: 0.7341
Epoch 40: val_accuracy improved from 0.98662 to 0.98757, saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras

Epoch 40: finished saving model to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
153/153 ━━━━━━━━━━━━━━━━━━━━ 15s 96ms/step - accuracy: 0.9440 - loss: 0.7332 - val_accuracy: 0.9876 - val_loss: 0.6149 - learning_rate: 1.0000e-04
Epoch 41/60
 33/153 ━━━━━━━━━━━━━━━━━━━━ 8s 67ms/step - accuracy: 0.9416 - loss: 0.7311
Corrupt JPEG data: bad Huffman code
 77/153 ━━━━━━━━━━━━━━━━━━━━ 5s 77ms/step - accuracy: 0.9450 - loss: 0.7311
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9461 - loss: 0.7309
Epoch 41: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9476 - loss: 0.7281 - val_accuracy: 0.9828 - val_loss: 0.6191 - learning_rate: 1.0000e-04
Epoch 42/60
 34/153 ━━━━━━━━━━━━━━━━━━━━ 9s 82ms/step - accuracy: 0.9547 - loss: 0.7103
Corrupt JPEG data: bad Huffman code
 76/153 ━━━━━━━━━━━━━━━━━━━━ 5s 75ms/step - accuracy: 0.9456 - loss: 0.7218
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9441 - loss: 0.7269
Epoch 42: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 83ms/step - accuracy: 0.9464 - loss: 0.7277 - val_accuracy: 0.9857 - val_loss: 0.6179 - learning_rate: 1.0000e-04
Epoch 43/60
 30/153 ━━━━━━━━━━━━━━━━━━━━ 10s 84ms/step - accuracy: 0.9397 - loss: 0.7041
Corrupt JPEG data: bad Huffman code
 67/153 ━━━━━━━━━━━━━━━━━━━━ 6s 75ms/step - accuracy: 0.9422 - loss: 0.7177
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9454 - loss: 0.7238
Epoch 43: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9501 - loss: 0.7242 - val_accuracy: 0.9857 - val_loss: 0.6156 - learning_rate: 1.0000e-04
Epoch 44/60
 37/153 ━━━━━━━━━━━━━━━━━━━━ 9s 86ms/step - accuracy: 0.9436 - loss: 0.7309 
Corrupt JPEG data: bad Huffman code
 80/153 ━━━━━━━━━━━━━━━━━━━━ 5s 79ms/step - accuracy: 0.9452 - loss: 0.7302
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 73ms/step - accuracy: 0.9444 - loss: 0.7302
Epoch 44: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 87ms/step - accuracy: 0.9436 - loss: 0.7278 - val_accuracy: 0.9866 - val_loss: 0.6154 - learning_rate: 1.0000e-04
Epoch 45/60
 35/153 ━━━━━━━━━━━━━━━━━━━━ 10s 89ms/step - accuracy: 0.9404 - loss: 0.7367
Corrupt JPEG data: bad Huffman code
 73/153 ━━━━━━━━━━━━━━━━━━━━ 6s 80ms/step - accuracy: 0.9442 - loss: 0.7334
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 74ms/step - accuracy: 0.9477 - loss: 0.7307
Epoch 45: val_accuracy did not improve from 0.98757

Epoch 45: ReduceLROnPlateau reducing learning rate to 1.9999999494757503e-05.
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 87ms/step - accuracy: 0.9501 - loss: 0.7272 - val_accuracy: 0.9828 - val_loss: 0.6205 - learning_rate: 1.0000e-04
Epoch 46/60
 34/153 ━━━━━━━━━━━━━━━━━━━━ 7s 66ms/step - accuracy: 0.9486 - loss: 0.7205
Corrupt JPEG data: bad Huffman code
 68/153 ━━━━━━━━━━━━━━━━━━━━ 5s 66ms/step - accuracy: 0.9491 - loss: 0.7207
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 65ms/step - accuracy: 0.9501 - loss: 0.7230
Epoch 46: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 13s 79ms/step - accuracy: 0.9507 - loss: 0.7220 - val_accuracy: 0.9866 - val_loss: 0.6155 - learning_rate: 2.0000e-05
Epoch 47/60
 40/153 ━━━━━━━━━━━━━━━━━━━━ 8s 75ms/step - accuracy: 0.9507 - loss: 0.7353
Corrupt JPEG data: bad Huffman code
 70/153 ━━━━━━━━━━━━━━━━━━━━ 6s 77ms/step - accuracy: 0.9503 - loss: 0.7324
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9493 - loss: 0.7299
Epoch 47: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 86ms/step - accuracy: 0.9470 - loss: 0.7256 - val_accuracy: 0.9857 - val_loss: 0.6153 - learning_rate: 2.0000e-05
Epoch 48/60
 32/153 ━━━━━━━━━━━━━━━━━━━━ 8s 70ms/step - accuracy: 0.9311 - loss: 0.7394
Corrupt JPEG data: bad Huffman code
 77/153 ━━━━━━━━━━━━━━━━━━━━ 5s 74ms/step - accuracy: 0.9393 - loss: 0.7374
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 73ms/step - accuracy: 0.9450 - loss: 0.7312
Epoch 48: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 88ms/step - accuracy: 0.9528 - loss: 0.7190 - val_accuracy: 0.9857 - val_loss: 0.6142 - learning_rate: 2.0000e-05
Epoch 49/60
 31/153 ━━━━━━━━━━━━━━━━━━━━ 8s 68ms/step - accuracy: 0.9430 - loss: 0.7231
Corrupt JPEG data: bad Huffman code
 69/153 ━━━━━━━━━━━━━━━━━━━━ 5s 71ms/step - accuracy: 0.9468 - loss: 0.7197
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9486 - loss: 0.7203
Epoch 49: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9495 - loss: 0.7192 - val_accuracy: 0.9857 - val_loss: 0.6142 - learning_rate: 2.0000e-05
Epoch 50/60
 32/153 ━━━━━━━━━━━━━━━━━━━━ 9s 79ms/step - accuracy: 0.9544 - loss: 0.7062
Corrupt JPEG data: bad Huffman code
 77/153 ━━━━━━━━━━━━━━━━━━━━ 5s 77ms/step - accuracy: 0.9543 - loss: 0.7156
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9544 - loss: 0.7201
Epoch 50: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 87ms/step - accuracy: 0.9556 - loss: 0.7218 - val_accuracy: 0.9857 - val_loss: 0.6141 - learning_rate: 2.0000e-05
Epoch 51/60
 33/153 ━━━━━━━━━━━━━━━━━━━━ 9s 75ms/step - accuracy: 0.9369 - loss: 0.7202
Corrupt JPEG data: bad Huffman code
 73/153 ━━━━━━━━━━━━━━━━━━━━ 5s 71ms/step - accuracy: 0.9399 - loss: 0.7260
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 68ms/step - accuracy: 0.9426 - loss: 0.7270
Epoch 51: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 83ms/step - accuracy: 0.9485 - loss: 0.7215 - val_accuracy: 0.9866 - val_loss: 0.6120 - learning_rate: 2.0000e-05
Epoch 52/60
 35/153 ━━━━━━━━━━━━━━━━━━━━ 9s 81ms/step - accuracy: 0.9490 - loss: 0.7133
Corrupt JPEG data: bad Huffman code
 69/153 ━━━━━━━━━━━━━━━━━━━━ 6s 76ms/step - accuracy: 0.9513 - loss: 0.7147
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9511 - loss: 0.7186
Epoch 52: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 13s 83ms/step - accuracy: 0.9515 - loss: 0.7192 - val_accuracy: 0.9866 - val_loss: 0.6140 - learning_rate: 2.0000e-05
Epoch 53/60
 31/153 ━━━━━━━━━━━━━━━━━━━━ 9s 78ms/step - accuracy: 0.9581 - loss: 0.7043
Corrupt JPEG data: bad Huffman code
 70/153 ━━━━━━━━━━━━━━━━━━━━ 6s 77ms/step - accuracy: 0.9552 - loss: 0.7114
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9532 - loss: 0.7162
Epoch 53: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9519 - loss: 0.7191 - val_accuracy: 0.9866 - val_loss: 0.6126 - learning_rate: 2.0000e-05
Epoch 54/60
 34/153 ━━━━━━━━━━━━━━━━━━━━ 7s 65ms/step - accuracy: 0.9428 - loss: 0.7223
Corrupt JPEG data: bad Huffman code
 72/153 ━━━━━━━━━━━━━━━━━━━━ 5s 67ms/step - accuracy: 0.9470 - loss: 0.7227
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
151/153 ━━━━━━━━━━━━━━━━━━━━ 0s 68ms/step - accuracy: 0.9497 - loss: 0.7232
Epoch 54: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 83ms/step - accuracy: 0.9519 - loss: 0.7192 - val_accuracy: 0.9866 - val_loss: 0.6121 - learning_rate: 2.0000e-05
Epoch 55/60
 35/153 ━━━━━━━━━━━━━━━━━━━━ 9s 81ms/step - accuracy: 0.9524 - loss: 0.7033 
Corrupt JPEG data: bad Huffman code
 65/153 ━━━━━━━━━━━━━━━━━━━━ 6s 78ms/step - accuracy: 0.9525 - loss: 0.7081
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9527 - loss: 0.7140
Epoch 55: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 20s 84ms/step - accuracy: 0.9528 - loss: 0.7169 - val_accuracy: 0.9837 - val_loss: 0.6128 - learning_rate: 2.0000e-05
Epoch 55: early stopping
Restoring model weights from the end of the best epoch: 40.

11. Fine-tuning Fase 3 — Full Unfreeze

Membuka semua layer base model. Learning rate 5e-5 (sangat kecil agar tidak merusak bobot pretrained).

In [23]:
# Unfreeze all layers in base model
for layer in base_model.layers:
    layer.trainable = True

model.compile(
    optimizer=AdamW(use_ema=True, ema_momentum=0.999,
                    learning_rate=5e-5, weight_decay=1e-4),
    loss=tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.2),
    metrics=['accuracy']
)

print("Fase 3: Full fine-tuning...")
history_3 = model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=90,
    initial_epoch=history_2.epoch[-1] + 1,
    callbacks=callbacks_list
)
Fase 3: Full fine-tuning...
Epoch 56/90
Corrupt JPEG data: bad Huffman code
2026-06-11 17:22:10.365783: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:22:10.516772: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:22:11.003935: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:22:11.170998: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:22:15.705420: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:22:15.856916: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
 48/153 ━━━━━━━━━━━━━━━━━━━━ 5s 52ms/step - accuracy: 0.8750 - loss: 0.8173
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 55ms/step - accuracy: 0.8887 - loss: 0.8050
2026-06-11 17:23:02.858842: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:23:03.009825: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:23:03.495720: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:23:03.662273: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:23:07.775573: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:23:07.927092: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 351ms/step - accuracy: 0.8887 - loss: 0.8049
Epoch 56: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 157s 417ms/step - accuracy: 0.9000 - loss: 0.7896 - val_accuracy: 0.9646 - val_loss: 0.6448 - learning_rate: 5.0000e-05
Epoch 57/90
 35/153 ━━━━━━━━━━━━━━━━━━━━ 9s 80ms/step - accuracy: 0.9124 - loss: 0.7628
Corrupt JPEG data: bad Huffman code
 99/153 ━━━━━━━━━━━━━━━━━━━━ 4s 75ms/step - accuracy: 0.9152 - loss: 0.7619
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9167 - loss: 0.7615
Epoch 57: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9213 - loss: 0.7588 - val_accuracy: 0.9732 - val_loss: 0.6351 - learning_rate: 5.0000e-05
Epoch 58/90
 33/153 ━━━━━━━━━━━━━━━━━━━━ 8s 72ms/step - accuracy: 0.9371 - loss: 0.7455
Corrupt JPEG data: bad Huffman code
 73/153 ━━━━━━━━━━━━━━━━━━━━ 5s 73ms/step - accuracy: 0.9333 - loss: 0.7503
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 67ms/step - accuracy: 0.9298 - loss: 0.7535
Epoch 58: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 82ms/step - accuracy: 0.9260 - loss: 0.7548 - val_accuracy: 0.9761 - val_loss: 0.6319 - learning_rate: 5.0000e-05
Epoch 59/90
 33/153 ━━━━━━━━━━━━━━━━━━━━ 10s 87ms/step - accuracy: 0.9335 - loss: 0.7396
Corrupt JPEG data: bad Huffman code
 75/153 ━━━━━━━━━━━━━━━━━━━━ 6s 78ms/step - accuracy: 0.9295 - loss: 0.7522
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.9292 - loss: 0.7565
Epoch 59: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 21s 85ms/step - accuracy: 0.9321 - loss: 0.7544 - val_accuracy: 0.9771 - val_loss: 0.6291 - learning_rate: 5.0000e-05
Epoch 60/90
 32/153 ━━━━━━━━━━━━━━━━━━━━ 9s 75ms/step - accuracy: 0.9324 - loss: 0.7550
Corrupt JPEG data: bad Huffman code
 68/153 ━━━━━━━━━━━━━━━━━━━━ 6s 76ms/step - accuracy: 0.9322 - loss: 0.7533
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.9339 - loss: 0.7503
Epoch 60: val_accuracy did not improve from 0.98757

Epoch 60: ReduceLROnPlateau reducing learning rate to 9.999999747378752e-06.
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 87ms/step - accuracy: 0.9348 - loss: 0.7452 - val_accuracy: 0.9685 - val_loss: 0.6415 - learning_rate: 5.0000e-05
Epoch 61/90
 29/153 ━━━━━━━━━━━━━━━━━━━━ 10s 89ms/step - accuracy: 0.9491 - loss: 0.7270
Corrupt JPEG data: bad Huffman code
 70/153 ━━━━━━━━━━━━━━━━━━━━ 6s 76ms/step - accuracy: 0.9471 - loss: 0.7332
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9448 - loss: 0.7365
Epoch 61: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 85ms/step - accuracy: 0.9393 - loss: 0.7366 - val_accuracy: 0.9751 - val_loss: 0.6266 - learning_rate: 1.0000e-05
Epoch 62/90
 32/153 ━━━━━━━━━━━━━━━━━━━━ 9s 81ms/step - accuracy: 0.9465 - loss: 0.7358 
Corrupt JPEG data: bad Huffman code
 95/153 ━━━━━━━━━━━━━━━━━━━━ 4s 77ms/step - accuracy: 0.9391 - loss: 0.7420
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.9384 - loss: 0.7421
Epoch 62: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 90ms/step - accuracy: 0.9387 - loss: 0.7385 - val_accuracy: 0.9771 - val_loss: 0.6232 - learning_rate: 1.0000e-05
Epoch 63/90
 47/153 ━━━━━━━━━━━━━━━━━━━━ 8s 76ms/step - accuracy: 0.9410 - loss: 0.7559
Corrupt JPEG data: bad Huffman code
 69/153 ━━━━━━━━━━━━━━━━━━━━ 6s 76ms/step - accuracy: 0.9399 - loss: 0.7529
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.9388 - loss: 0.7501
Epoch 63: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 87ms/step - accuracy: 0.9403 - loss: 0.7426 - val_accuracy: 0.9742 - val_loss: 0.6271 - learning_rate: 1.0000e-05
Epoch 64/90
 35/153 ━━━━━━━━━━━━━━━━━━━━ 9s 84ms/step - accuracy: 0.9469 - loss: 0.7338 
Corrupt JPEG data: bad Huffman code
 76/153 ━━━━━━━━━━━━━━━━━━━━ 5s 77ms/step - accuracy: 0.9449 - loss: 0.7385
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 73ms/step - accuracy: 0.9452 - loss: 0.7382
Epoch 64: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 86ms/step - accuracy: 0.9436 - loss: 0.7356 - val_accuracy: 0.9771 - val_loss: 0.6201 - learning_rate: 1.0000e-05
Epoch 65/90
 29/153 ━━━━━━━━━━━━━━━━━━━━ 10s 85ms/step - accuracy: 0.9236 - loss: 0.7620
Corrupt JPEG data: bad Huffman code
 65/153 ━━━━━━━━━━━━━━━━━━━━ 6s 75ms/step - accuracy: 0.9298 - loss: 0.7583
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 69ms/step - accuracy: 0.9346 - loss: 0.7528
Epoch 65: val_accuracy did not improve from 0.98757

Epoch 65: ReduceLROnPlateau reducing learning rate to 1.9999999494757505e-06.
153/153 ━━━━━━━━━━━━━━━━━━━━ 13s 84ms/step - accuracy: 0.9407 - loss: 0.7423 - val_accuracy: 0.9761 - val_loss: 0.6246 - learning_rate: 1.0000e-05
Epoch 66/90
 36/153 ━━━━━━━━━━━━━━━━━━━━ 9s 82ms/step - accuracy: 0.9442 - loss: 0.7218
Corrupt JPEG data: bad Huffman code
 68/153 ━━━━━━━━━━━━━━━━━━━━ 6s 73ms/step - accuracy: 0.9456 - loss: 0.7259
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 69ms/step - accuracy: 0.9451 - loss: 0.7287
Epoch 66: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 84ms/step - accuracy: 0.9452 - loss: 0.7264 - val_accuracy: 0.9761 - val_loss: 0.6244 - learning_rate: 2.0000e-06
Epoch 67/90
 32/153 ━━━━━━━━━━━━━━━━━━━━ 8s 70ms/step - accuracy: 0.9512 - loss: 0.7123
Corrupt JPEG data: bad Huffman code
 65/153 ━━━━━━━━━━━━━━━━━━━━ 6s 70ms/step - accuracy: 0.9483 - loss: 0.7208
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9436 - loss: 0.7282
Epoch 67: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 83ms/step - accuracy: 0.9407 - loss: 0.7299 - val_accuracy: 0.9780 - val_loss: 0.6249 - learning_rate: 2.0000e-06
Epoch 68/90
 53/153 ━━━━━━━━━━━━━━━━━━━━ 7s 74ms/step - accuracy: 0.9455 - loss: 0.7280
Corrupt JPEG data: bad Huffman code
 67/153 ━━━━━━━━━━━━━━━━━━━━ 6s 72ms/step - accuracy: 0.9457 - loss: 0.7291
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 70ms/step - accuracy: 0.9452 - loss: 0.7308
Epoch 68: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 86ms/step - accuracy: 0.9442 - loss: 0.7296 - val_accuracy: 0.9780 - val_loss: 0.6240 - learning_rate: 2.0000e-06
Epoch 69/90
 43/153 ━━━━━━━━━━━━━━━━━━━━ 8s 77ms/step - accuracy: 0.9282 - loss: 0.7398
Corrupt JPEG data: bad Huffman code
 82/153 ━━━━━━━━━━━━━━━━━━━━ 5s 75ms/step - accuracy: 0.9330 - loss: 0.7367
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
153/153 ━━━━━━━━━━━━━━━━━━━━ 0s 72ms/step - accuracy: 0.9374 - loss: 0.7340
Epoch 69: val_accuracy did not improve from 0.98757
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 87ms/step - accuracy: 0.9423 - loss: 0.7319 - val_accuracy: 0.9751 - val_loss: 0.6239 - learning_rate: 2.0000e-06
Epoch 70/90
 31/153 ━━━━━━━━━━━━━━━━━━━━ 10s 86ms/step - accuracy: 0.9618 - loss: 0.7141
Corrupt JPEG data: bad Huffman code
 69/153 ━━━━━━━━━━━━━━━━━━━━ 6s 78ms/step - accuracy: 0.9542 - loss: 0.7217
Corrupt JPEG data: 75969 extraneous bytes before marker 0xd2
152/153 ━━━━━━━━━━━━━━━━━━━━ 0s 71ms/step - accuracy: 0.9460 - loss: 0.7293
Epoch 70: val_accuracy did not improve from 0.98757

Epoch 70: ReduceLROnPlateau reducing learning rate to 3.999999989900971e-07.
153/153 ━━━━━━━━━━━━━━━━━━━━ 14s 86ms/step - accuracy: 0.9380 - loss: 0.7345 - val_accuracy: 0.9751 - val_loss: 0.6271 - learning_rate: 2.0000e-06
Epoch 70: early stopping
Restoring model weights from the end of the best epoch: 56.

12. Plot Akurasi dan Loss (Gabungan Semua Fase)

In [24]:
# Gabungkan history
acc = history_1.history['accuracy'] + history_2.history['accuracy'] + history_3.history['accuracy']
val_acc = history_1.history['val_accuracy'] + history_2.history['val_accuracy'] + history_3.history['val_accuracy']
loss = history_1.history['loss'] + history_2.history['loss'] + history_3.history['loss']
val_loss = history_1.history['val_loss'] + history_2.history['val_loss'] + history_3.history['val_loss']

# Batas antar fase
boundary_1 = len(history_1.history['accuracy']) - 1
boundary_2 = boundary_1 + len(history_2.history['accuracy'])

plt.figure(figsize=(16, 6))

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy', linewidth=2)
plt.plot(val_acc, label='Validation Accuracy', linewidth=2)
plt.axvline(x=boundary_1, color='gray', linestyle='--', alpha=0.7, label='Fase 2 start')
plt.axvline(x=boundary_2, color='black', linestyle='--', alpha=0.7, label='Fase 3 start')
plt.legend(fontsize=12)
plt.title('Training & Validation Accuracy', fontsize=14)
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.grid(alpha=0.3)

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss', linewidth=2)
plt.plot(val_loss, label='Validation Loss', linewidth=2)
plt.axvline(x=boundary_1, color='gray', linestyle='--', alpha=0.7, label='Fase 2 start')
plt.axvline(x=boundary_2, color='black', linestyle='--', alpha=0.7, label='Fase 3 start')
plt.legend(fontsize=12)
plt.title('Training & Validation Loss', fontsize=14)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.grid(alpha=0.3)

plt.tight_layout()
plt.show()

13. Test Time Augmentation (TTA)

Menggunakan model terbaik (EMA) dan menerapkan augmentasi geometrik saat inferensi untuk meningkatkan akurasi.

In [25]:
# Load model terbaik
print(f"Loading best model from {checkpoint_path}...")
best_model = tf.keras.models.load_model(checkpoint_path, compile=False)

# Kumpulkan gambar test asli
raw_test_ds = tf.keras.utils.image_dataset_from_directory(
    test_dir, shuffle=False, batch_size=BATCH_SIZE, image_size=IMG_SIZE)

test_images = []
test_labels_true_raw = []
for images, labels in raw_test_ds.unbatch():
    test_images.append(images.numpy())
    test_labels_true_raw.append(labels.numpy())

test_images = np.array(test_images)
test_labels_true = tf.one_hot(np.array(test_labels_true_raw), NUM_CLASSES).numpy()

TTA_STEPS = 5
tta_predictions = []

for i in range(TTA_STEPS):
    aug_images = data_augmentation(test_images, training=True)
    aug_images = preprocess_input(aug_images)
    preds = best_model.predict(aug_images, batch_size=BATCH_SIZE, verbose=0)
    tta_predictions.append(preds)
    print(f"  TTA step {i+1}/{TTA_STEPS}")

mean_tta = np.mean(tta_predictions, axis=0)

test_preds = np.argmax(mean_tta, axis=1)
test_true = np.argmax(test_labels_true, axis=1)
tta_acc = np.mean(test_preds == test_true)

print(f"\nTest Accuracy (TTA {TTA_STEPS}x): {tta_acc*100:.2f}%")
Loading best model from /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras...
Found 1052 files belonging to 4 classes.
2026-06-11 17:27:25.283775: I external/local_xla/xla/stream_executor/cuda/subprocess_compilation.cc:346] ptxas warning : Registers are spilled to local memory in function 'gemm_fusion_dot_2352', 36 bytes spill stores, 36 bytes spill loads

2026-06-11 17:27:26.774709: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:26.931108: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:27.736506: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:27.900098: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:28.454775: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:28.605712: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:29.033373: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:29.200411: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:30.126019: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:30.281824: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:30.478454: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
2026-06-11 17:27:30.636887: E external/local_xla/xla/stream_executor/cuda/cuda_timer.cc:86] Delay kernel timed out: measured time has sub-optimal accuracy. There may be a missing warmup execution, please investigate in Nsight Systems.
  TTA step 1/5
  TTA step 2/5
  TTA step 3/5
  TTA step 4/5
  TTA step 5/5

Test Accuracy (TTA 5x): 98.67%

14. Evaluasi — Classification Report & Confusion Matrix

In [26]:
print("\nClassification Report:\n")
print(classification_report(test_true, test_preds, target_names=class_names))

cm = confusion_matrix(test_true, test_preds)
plt.figure(figsize=(10, 8))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=class_names, yticklabels=class_names)
plt.title('Confusion Matrix', fontsize=14)
plt.xlabel('Predicted', fontsize=12)
plt.ylabel('True', fontsize=12)
plt.tight_layout()
plt.show()
Classification Report:

              precision    recall  f1-score   support

 Bercak Daun       0.98      0.96      0.97       226
  Daun Sehat       1.00      1.00      1.00       325
  Hawar Daun       0.98      1.00      0.99       242
  Karat Daun       0.98      0.99      0.99       259

    accuracy                           0.99      1052
   macro avg       0.99      0.99      0.99      1052
weighted avg       0.99      0.99      0.99      1052

15. Simpan Model Akhir

In [27]:
# Simpan model training final (dengan EMA weights)
final_path = os.path.join(ckpt_dir, 'best_model.keras')
model.save(final_path)
print(f"Model saved to {final_path}")

# Simpan juga versi tanpa EMA untuk fallback
model.save(os.path.join(ckpt_dir, 'final_model.keras'))
print("Final model saved.")
Model saved to /home/asephs/ZeaVis-Edu/Machine_Learning/best_model/best_model.keras
Final model saved.

16. Export Model untuk Produksi

Setelah training selesai, jalankan pipeline ekspor secara berurutan:

1. SavedModel + TFLite

python save_model.py

Memuat best_model/best_model.keras, membangun arsitektur bersih (tanpa augmentasi), dan mengekspor ke:

  • model/saved_model/ — format produksi
  • model/model.tflite — untuk perangkat mobile/edge

2. ONNX (Rust ML Service)

python convert_onnx.py

Mengonversi SavedModel ke model/model.onnx untuk digunakan oleh Rust/Axum/ONNX Runtime.

3. TensorFlow.js (Web)

export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
tensorflowjs_converter   --input_format=tf_saved_model   --output_format=tfjs_graph_model   --signature_name=serving_default   --saved_model_tags=serve   model/saved_model   model/tfjs_model

Catatan: Pipeline ekspor terpisah dari notebook karena save_model.py membangun ulang arsitektur tanpa layer training (GaussianNoise, augmentasi) untuk produksi.

17. Model Card — ZeaVis Edu Corn Disease Classifier

Atribut Detail
Nama Model ZeaVis Edu — EfficientNetV2B0 Classifier
Versi 2.0
Arsitektur EfficientNetV2B0 (Transfer Learning) + GaussianNoise + Conv2D(512) + Conv2D(256) + Dense(1024)
Framework TensorFlow 2.x / Keras (float32)
Dataset ~6000-8000 gambar daun jagung (4 kelas)
Kelas Bercak Daun, Hawar Daun, Karat Daun, Daun Sehat
Input Gambar RGB 224×224 piksel
Output Probabilitas per kelas (softmax)
Augmentasi Flip, Rotation, Zoom, Translation, Contrast, Brightness, MixUp, CutMix, RandomErasing, GaussianNoise
Optimizer AdamW + EMA + Label Smoothing 0.2
Training 3 fase: Head (lr=1e-3) → Partial FT (lr=1e-4) → Full FT (lr=5e-5)
Target Akurasi ≥95% test accuracy
Cara Pakai Upload gambar daun jagung → model memprediksi kelas penyakit
Etika Model ini hanya untuk tujuan edukasi/penelitian pertanian. Jangan gunakan sebagai satu-satunya alat diagnosis. Konsultasi dengan ahli pertanian tetap diperlukan.

© ZeaVis Edu — Proyek klasifikasi penyakit daun jagung

18. Upload ke Hugging Face

Upload model, TFLite, ONNX, dan TF.js ke Hugging Face Hub repo zeavis-edu/corn-leaf-disease-classifier.

Persyaratan: Token Hugging Face harus disetel sebagai environment variable HF_TOKEN.

  • Di Colab: buka Secrets manager (ikon 🔑 di panel kiri), tambahkan secret dengan nama HF_TOKEN dan isi dengan token Anda, lalu aktifkan akses Notebook.
  • Di lokal: export HF_TOKEN=hf_... atau simpan di .env.

Jalankan cell berikut untuk memulai pipeline upload otomatis.