Langsung ke konten
KamusNgoding
Mahir Deep-learning 5 menit baca

Optimasi Hyperparameter Tuning di Keras: Tips dan Trik Pro

#hyperparameter tuning #keras #tensorflow #optimasi

Optimasi Hyperparameter Tuning di Keras: Tips dan Trik Pro

Pendahuluan: Mengapa Optimasi Hyperparameter Krusial?

Bayangkan kamu sedang membangun sistem rekomendasi seperti yang digunakan Tokopedia — model yang salah dikonfigurasi bisa menghasilkan rekomendasi yang buruk meskipun arsitekturnya sudah benar. Di sinilah hyperparameter tuning menjadi pembeda antara model biasa dan model yang benar-benar andal.

Hyperparameter adalah nilai konfigurasi yang kamu tetapkan sebelum training dimulai — berbeda dengan parameter model (bobot) yang dipelajari selama training. Learning rate, jumlah layer, ukuran batch, dan dropout rate semuanya adalah hyperparameter. Memilih nilai yang tepat bisa meningkatkan akurasi model dari 70% menjadi 90% pada dataset yang sama.

Artikel ini membahas strategi tuning tingkat lanjut menggunakan Keras dan KerasTuner, termasuk teknik-teknik yang dipakai di lingkungan produksi nyata.


Memahami Hyperparameter Utama di Keras

Sebelum melakukan tuning, penting untuk memahami hyperparameter mana yang paling berpengaruh:

HyperparameterDampakRentang Umum
Learning rateSangat tinggi1e-5 hingga 1e-1
Batch sizeTinggi16, 32, 64, 128
Dropout rateSedang0.2 – 0.5
Jumlah unit/neuronSedang32 – 512
OptimizerTinggiAdam, SGD, RMSprop
L2 regularizationSedang1e-4 hingga 1e-2

Contoh model Keras standar sebelum dioptimasi:

import tensorflow as tf
from tensorflow import keras

def build_baseline_model():
    model = keras.Sequential([
        keras.layers.Dense(128, activation='relu', input_shape=(784,)),
        keras.layers.Dropout(0.3),
        keras.layers.Dense(64, activation='relu'),
        keras.layers.Dense(10, activation='softmax')
    ])
    model.compile(
        optimizer=keras.optimizers.Adam(learning_rate=0.001),
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

Model di atas bekerja, tapi apakah learning_rate=0.001 dan 128 unit memang optimal? Belum tentu. Inilah yang akan kita temukan dengan tuning.


Teknik Populer untuk Hyperparameter Tuning

Grid search mencoba semua kombinasi nilai yang telah ditentukan. Cocok untuk ruang pencarian kecil, tapi biaya komputasinya meledak secara eksponensial.

from sklearn.model_selection import GridSearchCV
from scikeras.wrappers import KerasClassifier
import numpy as np
from tensorflow import keras

# Siapkan data dummy untuk contoh
X_train = np.random.rand(1000, 784)
y_train = np.random.randint(0, 10, 1000)

def create_model(learning_rate=0.001, dropout_rate=0.3):
    model = keras.Sequential([
        keras.layers.Dense(128, activation='relu', input_shape=(784,)),
        keras.layers.Dropout(dropout_rate),
        keras.layers.Dense(10, activation='softmax')
    ])
    model.compile(
        optimizer=keras.optimizers.Adam(learning_rate=learning_rate),
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

model = KerasClassifier(model=create_model, epochs=10, batch_size=32, verbose=0)

param_grid = {
    'model__learning_rate': [0.001, 0.01, 0.0001],
    'model__dropout_rate': [0.2, 0.3, 0.5],
    'batch_size': [32, 64]
}

grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=3, n_jobs=-1)
grid_result = grid.fit(X_train, y_train)

print(f"Best score: {grid_result.best_score_:.4f}")
print(f"Best params: {grid_result.best_params_}")

Random search memilih kombinasi secara acak dari distribusi yang ditentukan. Secara statistik, random search lebih efisien dari grid search untuk ruang dimensi tinggi — mirip konsepnya dengan pendekatan probabilistik dalam Greedy vs Dynamic Programming: Perbedaan dan Kapan Menggunakannya, di mana strategi pencarian yang tepat sangat mempengaruhi efisiensi solusi.

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import loguniform, uniform

param_dist = {
    'model__learning_rate': loguniform(1e-5, 1e-1),
    'model__dropout_rate': uniform(0.1, 0.5),
    'batch_size': [16, 32, 64, 128]
}

random_search = RandomizedSearchCV(
    estimator=model,
    param_distributions=param_dist,
    n_iter=20,  # hanya 20 kombinasi acak
    cv=3,
    n_jobs=-1,
    random_state=42
)
random_search.fit(X_train, y_train)

print(f"Best score: {random_search.best_score_:.4f}")
print(f"Best params: {random_search.best_params_}")

3. Bayesian Optimization

Pendekatan paling canggih: menggunakan hasil percobaan sebelumnya untuk memilih hyperparameter berikutnya yang paling menjanjikan. Inilah yang digunakan KerasTuner secara internal.


Menggunakan KerasTuner untuk Optimasi Otomatis

KerasTuner adalah library resmi dari tim Keras yang menyederhanakan proses tuning secara drastis.

Instalasi:

pip install keras-tuner

Mendefinisikan model yang bisa di-tune:

import keras_tuner as kt
from tensorflow import keras

def build_tunable_model(hp):
    model = keras.Sequential()

    # Tuning jumlah unit di layer pertama
    model.add(keras.layers.Dense(
        units=hp.Int('units_layer1', min_value=64, max_value=512, step=64),
        activation='relu',
        input_shape=(784,)
    ))

    # Tuning dropout rate
    model.add(keras.layers.Dropout(
        rate=hp.Float('dropout_1', min_value=0.1, max_value=0.5, step=0.1)
    ))

    # Tuning apakah perlu layer kedua
    if hp.Boolean('use_second_layer'):
        model.add(keras.layers.Dense(
            units=hp.Int('units_layer2', min_value=32, max_value=256, step=32),
            activation='relu'
        ))
        model.add(keras.layers.Dropout(
            rate=hp.Float('dropout_2', min_value=0.1, max_value=0.4, step=0.1)
        ))

    model.add(keras.layers.Dense(10, activation='softmax'))

    # Tuning learning rate dengan distribusi log
    learning_rate = hp.Float(
        'learning_rate',
        min_value=1e-5,
        max_value=1e-2,
        sampling='log'
    )

    # Tuning jenis optimizer
    optimizer_choice = hp.Choice('optimizer', ['adam', 'rmsprop', 'sgd'])
    if optimizer_choice == 'adam':
        optimizer = keras.optimizers.Adam(learning_rate=learning_rate)
    elif optimizer_choice == 'rmsprop':
        optimizer = keras.optimizers.RMSprop(learning_rate=learning_rate)
    else:
        optimizer = keras.optimizers.SGD(learning_rate=learning_rate, momentum=0.9)

    model.compile(
        optimizer=optimizer,
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

Menjalankan tuner dengan Bayesian Optimization:

import numpy as np

# Data contoh (ganti dengan dataset aslimu)
X_train = np.random.rand(5000, 784)
y_train = np.random.randint(0, 10, 5000)

tuner = kt.BayesianOptimization(
    build_tunable_model,
    objective='val_accuracy',
    max_trials=30,
    executions_per_trial=2,
    directory='tuning_results',
    project_name='mnist_optimization'
)

# Callback untuk efisiensi
early_stopping = keras.callbacks.EarlyStopping(
    monitor='val_accuracy',
    patience=5,
    restore_best_weights=True
)

tuner.search(
    X_train, y_train,
    epochs=50,
    validation_split=0.2,
    callbacks=[early_stopping]
)

# Ambil hyperparameter terbaik
best_hps = tuner.get_best_hyperparameters(num_trials=1)[0]
print(f"Units layer 1: {best_hps.get('units_layer1')}")
print(f"Learning rate: {best_hps.get('learning_rate'):.6f}")
print(f"Optimizer: {best_hps.get('optimizer')}")

# Build model final dengan hyperparameter terbaik
best_model = tuner.hypermodel.build(best_hps)

Contoh Kasus Nyata: Optimasi CNN untuk Klasifikasi Gambar

Misalnya kamu ingin membangun fitur visual search seperti yang ada di platform e-commerce besar — model CNN untuk klasifikasi gambar produk. Berikut pipeline tuning yang lengkap:

import numpy as np
import tensorflow as tf
from tensorflow import keras
import keras_tuner as kt

# Load dan preprocess data (contoh menggunakan CIFAR-10)
(X_train, y_train), (X_test, y_test) = keras.datasets.cifar10.load_data()
X_train = X_train.astype('float32') / 255.0
X_test = X_test.astype('float32') / 255.0

def build_cnn_tunable(hp):
    model = keras.Sequential()

    # Block konvolusi pertama
    model.add(keras.layers.Conv2D(
        filters=hp.Int('filters_1', 32, 128, step=32),
        kernel_size=hp.Choice('kernel_1', [3, 5]),
        activation='relu',
        padding='same',
        input_shape=(32, 32, 3)
    ))
    model.add(keras.layers.BatchNormalization())
    model.add(keras.layers.MaxPooling2D(2, 2))

    # Block konvolusi kedua
    model.add(keras.layers.Conv2D(
        filters=hp.Int('filters_2', 64, 256, step=64),
        kernel_size=3,
        activation='relu',
        padding='same'
    ))
    model.add(keras.layers.BatchNormalization())
    model.add(keras.layers.MaxPooling2D(2, 2))

    model.add(keras.layers.Flatten())

    # Dense layer dengan tuning
    model.add(keras.layers.Dense(
        units=hp.Int('dense_units', 128, 512, step=128),
        activation='relu',
        kernel_regularizer=keras.regularizers.l2(
            hp.Float('l2_reg', 1e-5, 1e-2, sampling='log')
        )
    ))
    model.add(keras.layers.Dropout(
        hp.Float('dropout', 0.2, 0.5, step=0.1)
    ))
    model.add(keras.layers.Dense(10, activation='softmax'))

    model.compile(
        optimizer=keras.optimizers.Adam(
            learning_rate=hp.Float('lr', 1e-4, 1e-2, sampling='log')
        ),
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

# Setup tuner dengan Hyperband (lebih cepat dari Bayesian untuk CNN)
tuner = kt.Hyperband(
    build_cnn_tunable,
    objective='val_accuracy',
    max_epochs=30,
    factor=3,
    directory='cnn_tuning',
    project_name='cifar10_cnn'
)

callbacks = [
    keras.callbacks.EarlyStopping(monitor='val_loss', patience=5),
    keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3)
]

tuner.search(
    X_train, y_train,
    epochs=30,
    validation_split=0.2,
    callbacks=callbacks
)

# Training final model
best_hps = tuner.get_best_hyperparameters()[0]
final_model = tuner.hypermodel.build(best_hps)

history = final_model.fit(
    X_train, y_train,
    epochs=50,
    validation_split=0.2,
    callbacks=callbacks
)

test_loss, test_acc = final_model.evaluate(X_test, y_test)
print(f"Test accuracy setelah tuning: {test_acc:.4f}")

Dengan pendekatan ini, biasanya akurasi meningkat 5-15% dibanding model dengan hyperparameter default — perbedaan yang sangat signifikan di lingkungan produksi.


Troubleshooting: Error yang Sering Muncul

ResourceExhaustedError: OOM saat Menjalankan Banyak Trial

Penyebab: Memory GPU/RAM habis karena KerasTuner tidak membersihkan session Keras antar trial secara otomatis di beberapa versi.

Solusi:

import gc
import tensorflow as tf
from tensorflow import keras
import keras_tuner as kt

def build_model_with_cleanup(hp):
    # Bersihkan session sebelum build
    tf.keras.backend.clear_session()
    gc.collect()

    model = keras.Sequential([
        keras.layers.Dense(
            units=hp.Int('units', 64, 256, step=64),
            activation='relu',
            input_shape=(784,)
        ),
        keras.layers.Dropout(hp.Float('dropout', 0.2, 0.5, step=0.1)),
        keras.layers.Dense(10, activation='softmax')
    ])
    model.compile(
        optimizer=keras.optimizers.Adam(
            learning_rate=hp.Float('lr', 1e-4, 1e-2, sampling='log')
        ),
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

# Set memory growth untuk GPU agar tidak mengalokasikan semua VRAM sekaligus
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
    for gpu in gpus:
        tf.config.experimental.set_memory_growth(gpu, True)

Tuner Tidak Konvergen: Semua Trial Menghasilkan Akurasi Rendah

Penyebab: Rentang pencarian hyperparameter terlalu luas atau data belum dinormalisasi dengan benar, sehingga tuner kesulitan menemukan pola.

Solusi:

from sklearn.preprocessing import StandardScaler
import numpy as np

# Perbaiki: persempit rentang berdasarkan domain knowledge
# Gunakan 1e-4 hingga 1e-2, bukan 1e-7 hingga 1
# hp.Float('learning_rate', min_value=1e-4, max_value=1e-2, sampling='log')

# Pastikan normalisasi data benar
X_train_raw = np.random.rand(1000, 784)  # ganti dengan data aslimu
X_test_raw = np.random.rand(200, 784)

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train_raw)
X_test_scaled = scaler.transform(X_test_raw)  # WAJIB transform, bukan fit_transform

Trial Terhenti di Tengah Jalan tanpa Error Jelas

Penyebab: Callback EarlyStopping dengan patience terlalu rendah menghentikan training sebelum model sempat belajar, terutama di epoch-epoch awal.

Solusi:

from tensorflow import keras
import keras_tuner as kt

# Hindari patience terlalu kecil saat tuning
early_stopping = keras.callbacks.EarlyStopping(
    monitor='val_loss',
    patience=10,          # naikkan dari 3-5 ke minimal 8-10
    min_delta=0.001,      # tambahkan minimum delta untuk menghindari noise
    restore_best_weights=True
)

# Untuk Hyperband tuner, sesuaikan max_epochs agar trial punya cukup waktu
tuner = kt.Hyperband(
    build_cnn_tunable,
    objective='val_accuracy',
    max_epochs=50,
    factor=3,
    directory='cnn_tuning',
    project_name='cifar10_fixed'
)

Hasil Tuning Tidak Reproducible

Penyebab: Seed random tidak di-set secara konsisten di semua library yang digunakan.

Solusi:

import os
import random
import numpy as np
import tensorflow as tf

def set_all_seeds(seed=42):
    os.environ['PYTHONHASHSEED'] = str(seed)
    random.seed(seed)
    np.random.seed(seed)
    tf.random.set_seed(seed)
    os.environ['TF_DETERMINISTIC_OPS'] = '1'

set_all_seeds(42)

# Jalankan tuner setelah set seed
# tuner.search(X_train, y_train, ...)

Pertanyaan yang Sering Diajukan

Apa perbedaan antara hyperparameter dan parameter model?

Parameter model (bobot dan bias) dipelajari secara otomatis selama proses training melalui backpropagation. Hyperparameter adalah konfigurasi yang kamu tentukan sebelum training dimulai — seperti berapa learning rate, berapa layer yang digunakan, atau seberapa besar dropout. Hyperparameter mengontrol bagaimana model belajar, bukan apa yang dipelajari.

Berapa banyak trial yang dibutuhkan untuk hasil tuning yang baik?

Untuk model sederhana (dense network), 20-30 trial biasanya sudah cukup dengan Bayesian optimization. Untuk CNN atau model kompleks, 50-100 trial lebih ideal. Gunakan Hyperband jika resource terbatas karena algoritma ini menghentikan trial yang tidak menjanjikan lebih awal, sehingga bisa mengeksplorasi lebih banyak kombinasi dalam waktu yang sama.

Bagaimana cara menyimpan dan memuat hasil tuning agar tidak perlu diulang?

import keras_tuner as kt

# KerasTuner otomatis menyimpan hasil ke direktori yang ditentukan
tuner = kt.BayesianOptimization(
    build_tunable_model,
    objective='val_accuracy',
    max_trials=30,
    directory='saved_tuning',     # hasil disimpan di sini
    project_name='my_project',
    overwrite=False               # False = lanjutkan dari checkpoint
)

# Di session berikutnya, tuner akan otomatis melanjutkan dari trial terakhir
# tuner.search(X_train, y_train, ...)

Mengapa learning rate adalah hyperparameter yang paling penting untuk dioptimasi?

Learning rate menentukan seberapa besar langkah update bobot di setiap iterasi. Nilai terlalu besar menyebabkan training tidak stabil (loss meledak), sedangkan nilai terlalu kecil membuat training sangat lambat dan mudah terjebak di local minimum. Dibanding hyperparameter lain, perubahan kecil pada learning rate sering menghasilkan dampak yang jauh lebih besar pada performa model akhir.

Apakah hyperparameter tuning selalu diperlukan di setiap proyek?

Tidak selalu. Untuk proof of concept atau dataset kecil, hyperparameter default Keras (Adam dengan lr=0.001) sering sudah cukup baik. Tuning menjadi krusial ketika kamu sudah punya baseline yang bekerja dan ingin memaksimalkan performa untuk produksi, atau ketika perbedaan 1-2% akurasi punya dampak bisnis yang signifikan.


Kesimpulan

Hyperparameter tuning bukan sekadar trial and error — ini adalah proses sistematis yang mengombinasikan domain knowledge, strategi pencarian yang tepat, dan alat yang sesuai. Dengan memahami dampak setiap hyperparameter, memilih teknik yang efisien (random search atau Bayesian optimization untuk kebanyakan kasus), dan memanfaatkan KerasTuner, kamu bisa membangun model deep learning yang jauh lebih optimal tanpa menebak-nebak.

Langkah selanjutnya: eksplorasi teknik lanjutan seperti Learning Rate Scheduling, Population Based Training (PBT), atau integrasi dengan platform MLOps. Untuk membangun intuisi yang lebih kuat tentang strategi optimasi secara umum, artikel Greedy vs Dynamic Programming: Perbedaan dan Kapan Menggunakannya bisa membantumu memahami trade-off antar pendekatan pencarian. Dan jika kamu tertarik mengotomatisasi pipeline AI yang lebih kompleks, konsep dalam Tutorial Menulis Prompt Efektif untuk Pemula juga relevan saat bekerja dengan LLM dalam pipeline ML-mu.

Selamat bereksperimen dan terus optimalkan modelmu! Jika ada pertanyaan atau menemukan teknik tuning baru yang menarik, jangan ragu untuk terus eksplorasi artikel lainnya di KamusNgoding — komunitas developer Indonesia siap belajar bersama denganmu.

Artikel Terkait