Langsung ke konten
KamusNgoding
Menengah Deep-learning 6 menit baca

Cara Mengatasi Overfitting: Solusi Lengkap di TensorFlow

#overfitting #tensorflow #deep learning #regularization

Cara Mengatasi Overfitting: Solusi Lengkap di TensorFlow

Pendahuluan: Apa itu Overfitting dan Mengapa Penting?

Pernah melatih model deep learning yang akurasinya 99% di data training, tapi begitu dicoba di data nyata hasilnya amburadul? Itulah overfitting — salah satu musuh terbesar para praktisi machine learning.

Overfitting terjadi ketika model terlalu “hafal” data training, termasuk noise dan keanehan yang tidak relevan, sehingga gagal melakukan generalisasi ke data baru. Model seperti ini pintar di kelas tapi gagal di dunia nyata.

Bayangkan kamu sedang membangun sistem rekomendasi seperti yang digunakan Tokopedia atau Shopee — jika model overfit, ia hanya bisa merekomendasikan produk dengan baik untuk user yang datanya sudah pernah dilihat, tapi untuk user baru atau pola beli yang berbeda, hasilnya bisa sangat buruk.

Artikel ini akan membahas cara mendeteksi, memahami, dan mengatasi overfitting secara praktis menggunakan TensorFlow dan Keras. Sebelum masuk ke teknis, pastikan kamu sudah memahami konsep dasar Apa itu Machine Learning? Penjelasan Lengkap untuk Pemula karena kita akan membangun di atas fondasi tersebut.


Mengidentifikasi Kapan Model Anda Mengalami Overfitting

Tanda paling jelas overfitting adalah gap besar antara training accuracy dan validation accuracy. Cara paling mudah mendeteksinya adalah dengan memplot kurva training:

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt

def plot_training_history(history):
    fig, axes = plt.subplots(1, 2, figsize=(14, 5))

    # Plot Accuracy
    axes[0].plot(history.history['accuracy'], label='Training Accuracy')
    axes[0].plot(history.history['val_accuracy'], label='Validation Accuracy')
    axes[0].set_title('Model Accuracy')
    axes[0].set_xlabel('Epoch')
    axes[0].set_ylabel('Accuracy')
    axes[0].legend()

    # Plot Loss
    axes[1].plot(history.history['loss'], label='Training Loss')
    axes[1].plot(history.history['val_loss'], label='Validation Loss')
    axes[1].set_title('Model Loss')
    axes[1].set_xlabel('Epoch')
    axes[1].set_ylabel('Loss')
    axes[1].legend()

    plt.tight_layout()
    plt.show()

Ciri-ciri overfitting yang terlihat dari kurva:

  • Training loss terus turun, tapi validation loss mulai naik setelah beberapa epoch
  • Gap antara training accuracy dan validation accuracy semakin lebar seiring bertambahnya epoch
  • Model mencapai ~99% training accuracy tapi hanya ~70% validation accuracy

Teknik Utama untuk Mengatasi Overfitting

Ada beberapa pendekatan yang terbukti efektif:

1. Dropout

Secara acak “mematikan” sejumlah neuron selama training, memaksa model belajar representasi yang lebih robust.

2. L1 dan L2 Regularization

Menambahkan penalti ke fungsi loss berdasarkan besar bobot model:

  • L2 (Ridge): Menghukum bobot yang besar, mendorong bobot kecil merata
  • L1 (Lasso): Mendorong banyak bobot menjadi tepat nol (sparse model)

3. Early Stopping

Menghentikan training ketika validation loss tidak lagi membaik, mencegah model terus “menghafal” data.

4. Data Augmentation

Memperbanyak data training secara artifisial dengan transformasi gambar, sangat efektif untuk computer vision.

5. Batch Normalization

Menormalkan aktivasi di tiap layer, menstabilkan training dan sedikit memberikan efek regularisasi.


Implementasi Solusi Overfitting dengan TensorFlow dan Keras

Dropout Layer

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

def build_model_with_dropout(input_shape, num_classes):
    model = keras.Sequential([
        layers.Input(shape=input_shape),
        layers.Dense(256, activation='relu'),
        layers.Dropout(0.4),           # Matikan 40% neuron saat training
        layers.Dense(128, activation='relu'),
        layers.Dropout(0.3),           # Matikan 30% neuron saat training
        layers.Dense(64, activation='relu'),
        layers.Dropout(0.2),
        layers.Dense(num_classes, activation='softmax')
    ])
    return model

model = build_model_with_dropout((784,), 10)
model.summary()

L2 Regularization

from tensorflow.keras import regularizers

def build_model_with_l2(input_shape, num_classes, l2_lambda=0.001):
    model = keras.Sequential([
        layers.Input(shape=input_shape),
        layers.Dense(
            256,
            activation='relu',
            kernel_regularizer=regularizers.l2(l2_lambda)
        ),
        layers.Dense(
            128,
            activation='relu',
            kernel_regularizer=regularizers.l2(l2_lambda)
        ),
        layers.Dense(num_classes, activation='softmax')
    ])
    return model

Early Stopping dengan ModelCheckpoint

from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau

def get_callbacks(model_path='best_model.keras'):
    callbacks = [
        EarlyStopping(
            monitor='val_loss',
            patience=10,           # Tunggu 10 epoch sebelum berhenti
            restore_best_weights=True,  # Kembalikan bobot terbaik
            verbose=1
        ),
        ModelCheckpoint(
            filepath=model_path,
            monitor='val_loss',
            save_best_only=True,   # Simpan hanya model terbaik
            verbose=1
        ),
        ReduceLROnPlateau(
            monitor='val_loss',
            factor=0.5,            # Kurangi LR jadi setengah
            patience=5,
            min_lr=1e-7,
            verbose=1
        )
    ]
    return callbacks

Data Augmentation untuk Image Data

from tensorflow.keras.layers import RandomFlip, RandomRotation, RandomZoom

def build_augmentation_layer():
    """Layer augmentasi yang hanya aktif saat training."""
    return keras.Sequential([
        RandomFlip("horizontal"),
        RandomRotation(0.1),           # Rotasi ±10%
        RandomZoom(0.1),               # Zoom ±10%
    ], name="augmentation")

def build_cnn_with_augmentation(input_shape, num_classes):
    inputs = keras.Input(shape=input_shape)
    
    # Augmentasi hanya aktif saat training=True
    x = build_augmentation_layer()(inputs)
    
    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
    x = layers.BatchNormalization()(x)
    x = layers.MaxPooling2D((2, 2))(x)
    x = layers.Dropout(0.25)(x)
    
    x = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)
    x = layers.BatchNormalization()(x)
    x = layers.MaxPooling2D((2, 2))(x)
    x = layers.Dropout(0.25)(x)
    
    x = layers.Flatten()(x)
    x = layers.Dense(128, activation='relu', kernel_regularizer=regularizers.l2(0.001))(x)
    x = layers.Dropout(0.5)(x)
    outputs = layers.Dense(num_classes, activation='softmax')(x)
    
    return keras.Model(inputs, outputs)

Contoh Lengkap: Mengatasi Overfitting pada Klasifikasi Gambar CIFAR-10

Berikut contoh end-to-end yang bisa langsung dijalankan — mulai dari load data, membangun model dengan Dropout dan Regularization, hingga melatih dan mengevaluasinya:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, regularizers
import matplotlib.pyplot as plt
import numpy as np

# =============================================
# 1. Load dan Preprocessing Data
# =============================================
(X_train, y_train), (X_test, y_test) = keras.datasets.cifar10.load_data()

# Normalisasi ke rentang [0, 1]
X_train = X_train.astype('float32') / 255.0
X_test  = X_test.astype('float32') / 255.0

# One-hot encoding label
y_train = keras.utils.to_categorical(y_train, 10)
y_test  = keras.utils.to_categorical(y_test, 10)

print(f"Training data : {X_train.shape}")   # (50000, 32, 32, 3)
print(f"Test data     : {X_test.shape}")    # (10000, 32, 32, 3)

# =============================================
# 2. Bangun Model dengan Dropout + L2 + BatchNorm + Augmentation
# =============================================
def build_robust_model():
    inputs = keras.Input(shape=(32, 32, 3))

    # --- Augmentasi data (hanya aktif saat training) ---
    x = layers.RandomFlip("horizontal")(inputs)
    x = layers.RandomRotation(0.1)(x)

    # --- Blok Konvolusi 1 ---
    x = layers.Conv2D(64, (3, 3), padding='same',
                      kernel_regularizer=regularizers.l2(1e-4))(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)
    x = layers.Conv2D(64, (3, 3), padding='same',
                      kernel_regularizer=regularizers.l2(1e-4))(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)
    x = layers.MaxPooling2D((2, 2))(x)
    x = layers.Dropout(0.3)(x)

    # --- Blok Konvolusi 2 ---
    x = layers.Conv2D(128, (3, 3), padding='same',
                      kernel_regularizer=regularizers.l2(1e-4))(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)
    x = layers.Conv2D(128, (3, 3), padding='same',
                      kernel_regularizer=regularizers.l2(1e-4))(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)
    x = layers.MaxPooling2D((2, 2))(x)
    x = layers.Dropout(0.4)(x)

    # --- Classifier Head ---
    x = layers.GlobalAveragePooling2D()(x)   # Lebih robust dari Flatten
    x = layers.Dense(256, activation='relu',
                     kernel_regularizer=regularizers.l2(1e-4))(x)
    x = layers.Dropout(0.5)(x)
    outputs = layers.Dense(10, activation='softmax')(x)

    return keras.Model(inputs, outputs)

model = build_robust_model()
model.summary()

# =============================================
# 3. Compile
# =============================================
model.compile(
    optimizer=keras.optimizers.Adam(learning_rate=0.001),
    loss='categorical_crossentropy',
    metrics=['accuracy']
)

# =============================================
# 4. Training dengan Callbacks Anti-Overfitting
# =============================================
callbacks = [
    keras.callbacks.EarlyStopping(
        monitor='val_loss',
        patience=15,
        restore_best_weights=True,
        verbose=1
    ),
    keras.callbacks.ReduceLROnPlateau(
        monitor='val_loss',
        factor=0.5,
        patience=7,
        min_lr=1e-7,
        verbose=1
    ),
    keras.callbacks.ModelCheckpoint(
        filepath='best_cifar10_model.keras',
        monitor='val_loss',
        save_best_only=True,
        verbose=1
    )
]

history = model.fit(
    X_train, y_train,
    epochs=100,
    batch_size=64,
    validation_split=0.15,
    callbacks=callbacks,
    verbose=1
)

# =============================================
# 5. Evaluasi & Visualisasi
# =============================================
test_loss, test_acc = model.evaluate(X_test, y_test, verbose=0)
print(f"\nTest Accuracy : {test_acc:.4f}")
print(f"Test Loss     : {test_loss:.4f}")

# Plot kurva training
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

axes[0].plot(history.history['accuracy'],     label='Train Accuracy')
axes[0].plot(history.history['val_accuracy'], label='Val Accuracy')
axes[0].set_title('Accuracy per Epoch')
axes[0].set_xlabel('Epoch')
axes[0].set_ylabel('Accuracy')
axes[0].legend()

axes[1].plot(history.history['loss'],     label='Train Loss')
axes[1].plot(history.history['val_loss'], label='Val Loss')
axes[1].set_title('Loss per Epoch')
axes[1].set_xlabel('Epoch')
axes[1].set_ylabel('Loss')
axes[1].legend()

plt.tight_layout()
plt.show()

Dengan kombinasi teknik di atas, model biasanya mencapai ~85–88% test accuracy pada CIFAR-10 — jauh lebih baik dibandingkan model tanpa regularisasi yang mungkin overfit di 99% training accuracy tapi hanya 65–70% test accuracy.


Troubleshooting: Error yang Sering Muncul

ValueError: Shapes tidak cocok saat menggunakan Data Augmentation

Penyebab: Layer augmentasi seperti RandomFlip dan RandomRotation hanya menerima input 4D (batch, height, width, channels), tapi data yang dikirim belum berbentuk tensor dengan dimensi batch yang benar.

Solusi:

import tensorflow as tf
from tensorflow import keras

# Salah: data belum memiliki dimensi batch
image = tf.random.uniform((32, 32, 3))   # Shape: (32, 32, 3) → error

# Benar: tambahkan dimensi batch secara eksplisit
image = tf.expand_dims(image, axis=0)    # Shape: (1, 32, 32, 3) → OK

# Atau gunakan tf.data pipeline yang menangani batch otomatis
(X_train, y_train), _ = keras.datasets.cifar10.load_data()
X_train = X_train.astype('float32') / 255.0
y_train = keras.utils.to_categorical(y_train, 10)

dataset = tf.data.Dataset.from_tensor_slices((X_train, y_train))
dataset = dataset.shuffle(10000).batch(32).prefetch(tf.data.AUTOTUNE)
# Augmentasi layer akan menerima shape (32, H, W, C) secara otomatis

Model Berhenti Training Terlalu Cepat (EarlyStopping terlalu agresif)

Penyebab: Nilai patience terlalu kecil. Validation loss bisa berfluktuasi di awal training, sehingga EarlyStopping berhenti sebelum model sempat konvergen dengan baik.

Solusi:

from tensorflow.keras.callbacks import EarlyStopping

# Kurang tepat: patience terlalu kecil di awal training
early_stop_buruk = EarlyStopping(monitor='val_loss', patience=3)

# Lebih baik: naikkan patience dan tambahkan min_delta
early_stop = EarlyStopping(
    monitor='val_loss',
    patience=15,              # Minimal 10–20 untuk dataset sedang
    min_delta=0.001,          # Perubahan minimum yang dianggap "improvement"
    restore_best_weights=True,
    verbose=1
)

# Alternatif: mulai monitoring setelah beberapa epoch awal lewat
early_stop_delayed = EarlyStopping(
    monitor='val_loss',
    patience=10,
    start_from_epoch=20,      # Skip fluktuasi di 20 epoch pertama
    restore_best_weights=True
)

Dropout Membuat Model Tidak Bisa Konvergen (loss tidak turun)

Penyebab: Dropout rate terlalu tinggi (misalnya 0.7–0.8), menyebabkan terlalu banyak informasi hilang setiap forward pass sehingga gradient tidak mengalir dengan baik dan model tidak belajar sama sekali.

Solusi:

from tensorflow import keras
from tensorflow.keras import layers

# Rekomendasi dropout rate berdasarkan posisi layer:
model = keras.Sequential([
    layers.Input(shape=(784,)),

    # Layer awal: dropout rendah (0.1–0.2)
    layers.Dense(512, activation='relu'),
    layers.Dropout(0.2),

    # Layer tengah: dropout sedang (0.2–0.4)
    layers.Dense(256, activation='relu'),
    layers.Dropout(0.3),

    # Layer terakhir sebelum output: dropout lebih tinggi (0.4–0.5)
    layers.Dense(128, activation='relu'),
    layers.Dropout(0.5),

    layers.Dense(10, activation='softmax')
])

# Catatan: Dropout TIDAK aktif saat inference (predict/evaluate)
# model(x, training=False)  → Dropout dimatikan otomatis
# model(x, training=True)   → Dropout aktif (saat fit())

Pertanyaan yang Sering Diajukan

Apa perbedaan antara overfitting dan underfitting?

Overfitting terjadi ketika model terlalu kompleks dan “hafal” data training, menghasilkan akurasi training tinggi tapi validasi rendah. Sebaliknya, underfitting terjadi ketika model terlalu sederhana untuk menangkap pola dalam data, menghasilkan akurasi rendah di keduanya. Solusinya berlawanan: overfitting butuh regularisasi atau lebih banyak data, sedangkan underfitting butuh model yang lebih besar atau training yang lebih lama.

Berapa nilai Dropout rate yang ideal?

Tidak ada nilai universal — semuanya bergantung pada arsitektur dan ukuran dataset. Secara umum, 0.2–0.3 untuk layer awal/tengah dan 0.4–0.5 untuk layer terakhir sebelum output adalah titik awal yang baik. Lakukan eksperimen dengan validation set untuk menemukan nilai optimal. Jika model underfitting setelah menambahkan Dropout, kurangi rate-nya secara bertahap.

Apakah Batch Normalization bisa menggantikan Dropout?

Keduanya melakukan hal yang berbeda. Batch Normalization menormalkan distribusi aktivasi untuk mempercepat dan menstabilkan training, dengan efek regularisasi yang lebih lemah sebagai “bonus”. Dropout secara eksplisit memaksa redundansi representasi antar neuron. Untuk model CNN, BatchNorm sering lebih efektif; untuk fully connected layer, Dropout lebih umum dipakai. Menggunakan keduanya sekaligus bisa bekerja, tapi perlu hati-hati karena interaksinya bisa tidak terduga.

Mengapa model saya masih overfitting meskipun sudah pakai Dropout dan L2?

Kemungkinan besar akar masalahnya adalah kurangnya data training. Teknik regularisasi hanya bisa “memaksimalkan” generalisasi dari data yang ada — tidak bisa menciptakan informasi baru. Solusinya: tambahkan lebih banyak data (atau augmentasi lebih agresif), kurangi kompleksitas model (jumlah layer/neuron), atau pertimbangkan transfer learning menggunakan model pre-trained seperti ResNet atau EfficientNet yang sudah dilatih pada jutaan gambar.

Kapan sebaiknya menggunakan transfer learning daripada melatih dari awal?

Gunakan transfer learning ketika: data kamu sedikit (kurang dari 10.000 sampel), task kamu serupa dengan apa yang dipelajari model pre-trained (misalnya ImageNet untuk klasifikasi gambar umum), atau kamu memiliki keterbatasan komputasi. Melatih dari awal lebih masuk akal jika data kamu sangat domain-spesifik atau jumlahnya sangat besar. Jika ingin membangun sistem visual yang bisa langsung produksi, transfer learning hampir selalu menjadi pilihan lebih efisien daripada melatih dari nol.


Kesimpulan

Overfitting bukan akhir dari segalanya — ini adalah tantangan yang bisa diatasi dengan pendekatan yang sistematis. Ingat strategi utamanya:

  1. Deteksi dulu dengan memplot kurva training vs validation loss
  2. Dropout untuk mencegah ketergantungan antar neuron
  3. L2 Regularization untuk mengontrol besarnya bobot
  4. Early Stopping untuk menghentikan training di titik optimal
  5. Data Augmentation untuk memperkaya data training tanpa mengumpulkan data baru
  6. Batch Normalization untuk menstabilkan training

Kombinasikan beberapa teknik ini — tidak ada satu solusi ajaib yang bekerja untuk semua kasus. Selalu gunakan validation set untuk mengevaluasi efektivitas setiap teknik yang kamu coba. Untuk eksplorasi lebih dalam tentang arsitektur model dan optimasi, kamu juga bisa membaca tentang teknik deep learning lanjutan yang membahas strategi training yang lebih kompleks.

Selamat bereksperimen, dan jangan menyerah jika model pertamamu belum sempurna — justru di sanalah proses belajar yang sesungguhnya dimulai! Terus eksplorasi, terus coba, dan komunitas KamusNgoding selalu ada untuk menemanimu dalam perjalanan belajar deep learning ini.

Artikel Terkait