Rekayasa Fitur & Optimasi Model
Feature Scaling & Normalization
Penskalaan fitur adalah langkah penting sebelum melatih banyak model. Beberapa algoritma (KNN, SVM, K-Means, neural networks) sangat sensitif terhadap skala fitur — tanpa scaling, fitur dengan range besar akan mendominasi.
Perbedaan Utama: StandardScaler vs MinMaxScaler vs RobustScaler
StandardScaler (Z-score)
Mentransformasikan fitur sehingga memiliki mean = 0 dan std = 1. Cocok bila fitur mengikuti distribusi mendekati Gaussian. Formula: z = (x − μ) / σ.
MinMaxScaler
Memetakan nilai ke rentang [0, 1] (atau rentang custom). Baik untuk algoritma yang mengasumsikan rentang tertentu (misal jaringan syaraf dengan activation tertentu).
RobustScaler
Skala berdasarkan median dan IQR (interquartile range). Lebih tahan terhadap outlier dibanding Standard/MinMax.
Power Transform & Log
Transformasi non-linear (Box-Cox, Yeo-Johnson) berguna untuk membuat distribusi lebih Gaussian; log sering dipakai untuk mengurangi skewness fitur moneter.
Ilustrasi Interaktif — Dampak Scaling
Praktik Terbaik
- 1Selalu fit scaler pada training set saja. Lalu gunakan transform pada test/validation. Jangan pernah fit scaler pada seluruh data (data leakage).
- 2Pilih scaler sesuai model: KNN/SVM/K-Means → StandardScaler/MinMax; Tree-based → tidak perlu scaling biasanya; Outlier-heavy → RobustScaler.
- 3Simpan scaler (joblib.dump) bersama model agar pipeline produksi mereplikasi preprocessing yang sama.
Kode: Penerapan Scaling di Pipeline
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler from sklearn.pipeline import Pipeline # Contoh: Pipeline sederhana untuk model-sensitive-to-scale pipeline = Pipeline([ ('scaler', StandardScaler()), ('model', LogisticRegression(random_state=42)) ]) # Fit hanya pada training set: # pipeline.fit(X_train, y_train) # predictions = pipeline.predict(X_test)
🧠 Uji Pemahaman
Encoding Categorical Variables
Variabel kategorikal (seperti gender, negara, atau tipe produk) perlu diubah menjadi bentuk numerik agar model ML bisa memprosesnya. Ada banyak teknik encoding — pilihan tergantung pada jumlah kategori (cardinality), urutan (ordinalitas), dan model yang digunakan.
1) One-Hot Encoding
Membuat kolom biner untuk setiap kategori. Sangat intuitif dan bekerja baik untuk kategori yang sedikit (< 10–20). Namun dapat menyebabkan "dimensionality explosion" jika kategori sangat banyak.
from sklearn.preprocessing import OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline cat_cols = ['gender', 'contract_type'] preprocessor = ColumnTransformer([ ('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False), cat_cols) ], remainder='passthrough') pipeline = Pipeline([ ('pre', preprocessor), ('clf', RandomForestClassifier(random_state=42)) ])
Kapan Pakai
- •Gunakan jika cardinality rendah (misal < 20) dan model tidak sensitif terhadap banyak fitur baru.
- •Gunakan handle_unknown=’ignore’ agar data baru pada saat inference tidak menyebabkan error.
2) Ordinal Encoding
Mengubah kategori menjadi bilangan bulat berdasarkan urutan yang logis (misal: low < medium < high). Hati-hati: angka ini memperkenalkan bias ordinal ke model; hanya pakai jika ada makna urutan.
from sklearn.preprocessing import OrdinalEncoder ord_map = [['low', 'medium', 'high']] enc = OrdinalEncoder(categories=ord_map) X['size_ord'] = enc.fit_transform(X[['size']])
3) Target / Mean Encoding (with CV)
Mengganti kategori dengan rata-rata target pada kategori tersebut (misal probabilitas rata-rata label=1). Berisiko data leakage jika langsung menggunakan seluruh train set — gunakan teknik K-Fold target encoding untuk menghindari kebocoran.
import numpy as np import pandas as pd from sklearn.model_selection import KFold def target_encode_cv(train_series, target, n_splits=5): kf = KFold(n_splits=n_splits, shuffle=True, random_state=42) oof = pd.Series(np.nan, index=train_series.index) for tr_idx, val_idx in kf.split(train_series): tr_s = train_series.iloc[tr_idx] tr_t = target.iloc[tr_idx] means = tr_t.groupby(tr_s).mean() oof.iloc[val_idx] = train_series.iloc[val_idx].map(means) # Fill unseen categories with global mean oof.fillna(target.mean(), inplace=True) return oof # usage: X['cat_te'] = target_encode_cv(X['category'], y_train)
Kapan Pakai
- 1Cardinality tinggi (puluhan ribu kategori): target encoding mengurangi dimensi secara efektif.
- 2Penting: Gunakan K-Fold atau smoothing untuk menghindari overfitting / leakage.
4) Binary / Hashing Encoding
Metode binary mengubah kategori menjadi representasi biner (bit) — lebih compact dari one-hot. Hashing trick memetakan kategori ke sejumlah kolom fixed tanpa menyimpan dictionary kategori, berguna untuk data streaming atau cardinality sangat besar.
from sklearn.feature_extraction.text import HashingVectorizer hv = HashingVectorizer(n_features=32, alternate_sign=False) X_h = hv.transform(X['category_text'].astype('str'))
5) Count / Frequency Encoding
Gantikan kategori dengan frekuensi atau count kemunculannya. Sederhana dan seringkali kuat—misal fitur “rare” bisa jadi sinyal penting.
# Count encoding
counts = X['category'].value_counts()
X['category_cnt'] = X['category'].map(counts)
6) Practical Tips & Pitfalls
✅ Simpan mapping encoder
Simpan dictionary mapping (mis. category→mean) atau encoder object (joblib) bersama model untuk inference konsisten.
⚠️ Tangani kategori baru (unseen)
One-Hot: gunakan handle_unknown=’ignore’. Target encoding: isi dengan global mean atau smoothing.
Pipeline Best Practice
Selalu jadikan encoding bagian dari Pipeline atau ColumnTransformer. Dengan demikian cross-validation dan production inference akan mereplikasi preprocessing yang sama tanpa data leakage.
🧠 Uji Pemahaman
Feature Selection Techniques
Memilih fitur yang tepat membantu model lebih cepat, lebih sederhana, dan seringkali lebih akurat. Ada tiga keluarga teknik: Filter, Wrapper, dan Embedded.
Filter Methods — Cepat & Model-Agnostic
Filter mengevaluasi fitur berdasarkan statistik independen dari model, seperti korelasi, chi-squared, ANOVA, atau mutual information. Cocok untuk pra-seleksi awal.
from sklearn.feature_selection import SelectKBest, f_classif, mutual_info_classif # ANOVA F-test untuk fitur numerik selector = SelectKBest(score_func=f_classif, k=10) X_new = selector.fit_transform(X_train, y_train) # Mutual information untuk fitur baik numerik maupun kategorikal selector2 = SelectKBest(score_func=mutual_info_classif, k=15) X_new2 = selector2.fit_transform(X_train, y_train)
Wrapper Methods — Lebih Akurat tapi Mahal
Wrapper menggunakan model sebagai “black box” untuk mengevaluasi subset fitur (mis: RFE, forward selection). Metode ini cenderung memberikan subset fitur yang lebih baik namun memerlukan banyak training model.
from sklearn.feature_selection import RFE from sklearn.linear_model import LogisticRegression est = LogisticRegression(max_iter=1000) rfe = RFE(estimator=est, n_features_to_select=10) rfe.fit(X_train, y_train) selected_features = X_train.columns[rfe.get_support()] print(selected_features)
Embedded Methods — Seleksi Saat Training
Embedded methods menggabungkan pemilihan fitur dalam proses training model. Contoh populer: Lasso (L1) regularization, decision tree feature importance, dan feature importance dari ensembles (Random Forest, XGBoost).
from sklearn.linear_model import LogisticRegression # L1 regularization untuk pemilihan fitur (koef menjadi 0) clf = LogisticRegression(penalty='l1', solver='saga', max_iter=2000) clf.fit(X_train, y_train) coef = pd.Series(clf.coef_[0], index=X_train.columns) selected = coef[coef.abs() > 1e-4].index
Praktik: Gabungkan Teknik
Strategi yang umum: gunakan metode filter cepat untuk memangkas fitur (misal dari 1000 → 200), lalu pakai RFE atau embedded (Lasso / Tree importance) untuk memilih subset akhir.
Evaluasi Fitur Secara Praktis
Permutation Importance
Nilai seberapa besar performa model turun ketika fitur diacak. Lebih andal daripada MDI karena dihitung pada test set.
Stability Selection
Jalankan seleksi berulang pada bootstrap sample; pilih fitur yang sering muncul. Mengurangi variabilitas pilihan fitur.
from sklearn.inspection import permutation_importance results = permutation_importance(rf, X_test, y_test, n_repeats=30, random_state=42) perm_sorted_idx = results.importances_mean.argsort()[-10:] for i in perm_sorted_idx: print(X_test.columns[i], results.importances_mean[i].round(4))
🧠 Uji Pemahaman
Cross-Validation Explained
Cross-validation (CV) adalah teknik untuk menilai generalisasi model dan mengurangi risiko overfitting pada hyperparameter tuning. CV membagi data menjadi beberapa fold, melatih model pada sebagian dan menguji pada sisanya secara bergantian.
Jenis Cross-Validation yang Biasa Dipakai
K-Fold CV
Data dibagi menjadi K bagian (fold). Setiap fold bergantian menjadi validation set, sisanya menjadi training. Hasilnya adalah rata-rata skor across folds.
Stratified K-Fold
Versi K-Fold yang menjaga proporsi kelas di setiap fold — penting untuk dataset imbalanced pada klasifikasi.
TimeSeriesSplit
Untuk data berurutan (time series). Tidak boleh mengacak data temporal — train pada masa lalu, validasi pada masa depan.
Nested Cross-Validation
Metode terbaik untuk memilih model dan mengukur generalisasi sekaligus: ada CV luar untuk evaluasi dan CV dalam untuk tuning hyperparameter (Grid/Random).
Visualisasi CV (Ilustrasi K-Fold)
Kode: Contoh Stratified K-Fold CV
from sklearn.model_selection import StratifiedKFold, cross_val_score from sklearn.ensemble import RandomForestClassifier clf = RandomForestClassifier(random_state=42) skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(clf, X, y, cv=skf, scoring='f1') print(f"F1 CV: {scores.mean():.4f} ± {scores.std():.4f}")
Nested CV untuk Model Selection
Gunakan nested CV saat melakukan hyperparameter tuning untuk menghindari bias optimistik pada evaluasi. CV luar memberikan estimasi performa final, CV dalam melakukan pencarian hyperparameter.
from sklearn.model_selection import GridSearchCV, cross_val_score, StratifiedKFold from sklearn.ensemble import RandomForestClassifier param_grid = {'n_estimators':[50,100],'max_depth':[5,10]} clf = RandomForestClassifier(random_state=42) inner_cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=42) outer_cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) grid = GridSearchCV(clf, param_grid, cv=inner_cv, scoring='f1') nested_scores = cross_val_score(grid, X, y, cv=outer_cv, scoring='f1') print(f"Nested CV F1: {nested_scores.mean():.4f} ± {nested_scores.std():.4f}")
🧠 Uji Pemahaman
Hyperparameter Tuning (Grid & Random Search)
Hyperparameter tuning adalah proses mencari konfigurasi parameter terbaik untuk model. Dua teknik klasik: Grid Search (eksplorasi lengkap pada grid) dan Random Search (sampling acak dari distribusi). Untuk performa lebih baik dan resource efisien, ada juga Bayesian optimization (Optuna, Hyperopt).
Grid Search vs Random Search
Grid Search
Mencoba semua kombinasi dalam grid parameter. Bisa memberikan hasil optimal jika grid kecil, tetapi sangat mahal secara komputasi jika banyak parameter atau nilai.
Randomized Search
Sampling acak dari distribusi parameter selama N iterasi. Lebih efisien untuk ruang parameter besar — sering menemukan konfigurasi baik dengan jauh lebih sedikit evaluasi.
Kode: GridSearchCV (Contoh)
from sklearn.model_selection import GridSearchCV from sklearn.ensemble import RandomForestClassifier param_grid = { 'n_estimators': [50, 100, 200], 'max_depth' : [None, 10, 20], 'min_samples_leaf': [1, 2, 5] } clf = RandomForestClassifier(random_state=42) grid = GridSearchCV(clf, param_grid, cv=5, scoring='f1', n_jobs=-1, verbose=2) grid.fit(X_train, y_train) print(f"Best params: {grid.best_params_}") print(f"Best CV score: {grid.best_score_:.4f}")
Kode: RandomizedSearchCV (Contoh)
from sklearn.model_selection import RandomizedSearchCV from scipy.stats import randint param_dist = { 'n_estimators': randint(50, 500), 'max_depth' : [None]+list(range(5,31,5)), 'min_samples_leaf': randint(1, 6) } clf = RandomForestClassifier(random_state=42) rand_search = RandomizedSearchCV(clf, param_distributions=param_dist, n_iter=40, cv=5, scoring='f1', n_jobs=-1, random_state=42) rand_search.fit(X_train, y_train) print(f"Best params: {rand_search.best_params_}") print(f"Best CV score: {rand_search.best_score_:.4f}")
Praktik: Early Stopping & Resource Tips
Early Stopping
Untuk model boosting (XGBoost, LightGBM), gunakan early stopping pada validation set saat tuning untuk menghindari overfitting dan mengurangi waktu training.
Resource Tips
• Gunakan n_jobs untuk paralelisasi
• Untuk ruang besar, gunakan RandomizedSearch dulu untuk menemukan wilayah menjanjikan, lalu GridSearch sempit di wilayah itu
• Batasi n_iter agar waktu tidak meledak
Alternatif: Bayesian Optimization
Optuna dan Hyperopt mencari hyperparameter secara pintar menggunakan model probabilistik (Bayesian). Lebih efisien dibanding Random Search terutama pada ruang berdimensi tinggi.
# pip install optuna import optuna def objective(trial): n_estimators = trial.suggest_int('n_estimators', 50, 500) max_depth = trial.suggest_int('max_depth', 3, 30) clf = RandomForestClassifier(n_estimators=n_estimators, max_depth=max_depth, random_state=42) score = cross_val_score(clf, X_train, y_train, cv=3, scoring='f1').mean() return score study = optuna.create_study(direction='maximize') study.optimize(objective, n_trials=50) print(study.best_params)