Teori dan Implementasi Elastic Net Regression: Menggabungkan Regularisasi L1 dan L2 dengan Scikit-learn

Lhuqita Fazry
Machine Learning Scikit-learn Regression Regularization Elastic Net
Teori dan Implementasi Elastic Net Regression: Menggabungkan Regularisasi L1 dan L2 dengan Scikit-learn

Mengapa Ridge dan Lasso Saja Tidak Cukup

Ridge regression menambahkan penalty L2, yaitu kuadrat dari koefisien, ke dalam fungsi loss. Efeknya, setiap koefisien dikecilkan secara proporsional tanpa pernah benar-benar menjadi nol. Lasso regression menggunakan penalty L1, yaitu nilai absolut koefisien, sehingga sebagian koefisien terdorong tepat ke nol. Perbedaan inilah yang membuat Lasso mampu melakukan feature selection secara otomatis, sementara Ridge hanya menstabilkan estimasi.

Masalah muncul ketika dataset memiliki fitur yang saling berkorelasi kuat. Lasso cenderung memilih satu fitur dari kelompok yang berkorelasi secara arbitrer dan mengabaikan sisanya. Ridge justru mempertahankan semua fitur dengan koefisien yang terbagi rata, sehingga tidak ada fitur yang benar-benar dihilangkan. Kedua perilaku ini kurang ideal ketika kita menginginkan seleksi fitur sekaligus koefisien yang stabil.

Ilustrasi geometris norm L1 dan L2: solusi L1 jatuh di sudut diamond sehingga sparse, sedangkan solusi L2 jatuh di titik halus circle

Gambar: Balls untuk norm L1 dan L2 beserta irisan dengan fungsi objektif linear — Sumber: Wikimedia Commons

Elastic Net menjawab masalah ini dengan menggabungkan penalty L1 dan L2 dalam satu objective function. Model ini meminimalkan residual sum of squares ditambah kombinasi dari alpha dan l1_ratio. Hasilnya, Elastic Net mendapatkan efek sparsity dari Lasso dan efek grouping dari Ridge dalam satu waktu.

Situasi yang tepat memakai Elastic Net adalah dataset dengan fitur berkorelasi, kondisi p > n di mana jumlah fitur melebihi jumlah sampel, dan kebutuhan seleksi fitur bersamaan dengan koefisien yang stabil. Pada kondisi tersebut, Lasso bisa gagal karena memilih fitur secara tidak konsisten, sedangkan Ridge tidak memberikan solusi yang sparse. Kombinasi kedua penalty membuat Elastic Net lebih fleksibel untuk menyesuaikan tingkat sparsity sesuai struktur data.

Mekanisme Penalty Gabungan L1 dan L2

Scikit-learn mendefinisikan penalty Elastic Net sebagai alpha * l1_ratio * ||w||_1 + 0.5 * alpha * (1 - l1_ratio) * ||w||_2^2. Parameter l1_ratio berperan sebagai pengatur proporsi campuran antara kedua penalty. Nilai l1_ratio sama dengan 1 membuat model berperilaku persis seperti Lasso, sedangkan nilai mendekati 0 membuat model mendekati Ridge.

Efek grouping adalah keunggulan utama Elastic Net. Fitur yang berkorelasi mendapat koefisien yang mirip satu sama lain, berbeda dengan Lasso murni yang cenderung memilih satu dari kelompok tersebut. Sifat ini penting untuk dataset dengan fitur yang saling bergantung, misalnya variabel yang diukur dari sumber yang sama. Dalam praktik, efek grouping membuat koefisien kelompok fitur tersebut tetap seimbang, sehingga interpretasi model tidak bergantung pada satu fitur tunggal.

Secara geometris, region constraint Elastic Net adalah perpaduan antara diamond milik L1 dan circle milik L2. Bayangkan kontur RSS menyentuh batas region tersebut, titik kontak menghasilkan solusi yang sparse sekaligus berkelompok. Interpretasi ini membantu kita memahami mengapa Elastic Net tidak seagresif Lasso dalam memilih fitur. Ketika l1_ratio besar, bentuk region mendekati diamond sehingga solusi lebih sparse. Ketika l1_ratio kecil, region mendekati circle sehingga koefisien menyusut tanpa menghilang. Perubahan bentuk inilah yang membuat Elastic Net dapat disetel halus antara dua ekstrem tersebut.

Perbandingan region constraint L1 (diamond), L2 (circle), dan Elastic Net (rounded diamond)

Gambar: Region constraint L1 (diamond), L2 (circle), dan Elastic Net (rounded diamond) — Sumber: scikit-learn Documentation

Untuk melihat perbedaan perilaku secara konkret, kita bandingkan koefisien Ridge, Lasso, dan Elastic Net pada data sintetis dengan fitur berkorelasi.

python
!pip install scikit-learn numpy matplotlib

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_regression
from sklearn.linear_model import Ridge, Lasso, ElasticNet

rng = np.random.RandomState(42)
X, y = make_regression(n_samples=200, n_features=3, noise=0.5, random_state=42)
X[:, 1] = X[:, 0] + rng.normal(0, 0.1, size=200)
X[:, 2] = X[:, 0] + rng.normal(0, 0.1, size=200)

models = {
    "Ridge": Ridge(alpha=1.0),
    "Lasso": Lasso(alpha=0.1),
    "ElasticNet": ElasticNet(alpha=0.1, l1_ratio=0.5),
}

coefs = {}
for name, model in models.items():
    model.fit(X, y)
    coefs[name] = model.coef_

print(f"{'Model':<12} {'w1':>8} {'w2':>8} {'w3':>8}")
for name, c in coefs.items():
    print(f"{name:<12} {c[0]:>8.3f} {c[1]:>8.3f} {c[2]:>8.3f}")

fig, ax = plt.subplots(figsize=(8, 4))
x_pos = np.arange(3)
width = 0.25
for i, (name, c) in enumerate(coefs.items()):
    ax.bar(x_pos + i * width, c, width, label=name)
ax.set_xticks(x_pos + width)
ax.set_xticklabels(["w1", "w2", "w3"])
ax.axhline(0, color="black", linewidth=0.8)
ax.set_ylabel("Koefisien")
ax.legend()
plt.tight_layout()
plt.savefig("public/images/blog/teori-dan-implementasi-elastic-net-regression-regularisasi-l1-dan-l2-dengan-scikit-learn-output-1.png", dpi=150)
plt.show()

Output:

text
Model              w1       w2       w3
Ridge          84.539  -13.050   -6.227
Lasso         139.004  -44.306  -29.639
ElasticNet     31.378   16.057   17.142
Output dari kode di atas

Logika skrip ini sederhana: kita bangun tiga fitur yang saling berkorelasi, lalu fit ketiga model pada data yang sama. Tabel koefisien menunjukkan Lasso menekan sebagian koefisien ke nol, Ridge membagi bobot secara merata, dan Elastic Net berada di antara keduanya. Plot batang memperlihatkan perbedaan tersebut secara visual.

Implementasi Elastic Net dengan Scikit-learn

Kelas ElasticNet dari sklearn.linear_model adalah implementasi utama yang kita pakai. Parameter utama yang perlu dipahami adalah alpha untuk kekuatan regularisasi, l1_ratio untuk proporsi campuran, max_iter untuk jumlah iterasi maksimum, dan tol untuk toleransi konvergensi. Nilai max_iter yang terlalu kecil membuat optimasi berhenti sebelum konvergen, sehingga koefisien belum optimal. Parameter tol menentukan ambang perubahan koefisien antar iterasi; ketika perubahannya lebih kecil dari tol, proses dianggap selesai. Pada dataset besar, kita perlu menaikkan max_iter agar solusi benar-benar stabil.

Kita gunakan dataset dengan fitur yang saling berkorelasi agar efek grouping terlihat nyata. Efek grouping paling terlihat ketika dua fitur membawa informasi yang hampir sama, karena Elastic Net membagi bobot di antara keduanya alih-alih memilih salah satu. Data dibagi menjadi training dan test set dengan train_test_split, lalu di-standardize menggunakan StandardScaler. Standardisasi wajib dilakukan karena penalty L1 dan L2 sensitif terhadap skala fitur.

Alur kerja dimulai dari fit model pada data training, lalu prediksi data test, dan evaluasi dengan mean_squared_error serta r2_score. Kita bandingkan hasil Elastic Net dengan Ridge dan Lasso pada data yang sama untuk melihat trade-off masing-masing. Perbandingan ini penting karena memilih model bukan hanya soal akurasi, tetapi juga soal interpretabilitas koefisien.

python
!pip install scikit-learn numpy pandas

import numpy as np
import pandas as pd
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge, Lasso, ElasticNet
from sklearn.metrics import mean_squared_error, r2_score

X, y = make_regression(n_samples=300, n_features=6, noise=1.0, random_state=7)
X[:, 1] = X[:, 0] + np.random.normal(0, 0.1, size=300)
X[:, 3] = X[:, 2] + np.random.normal(0, 0.1, size=300)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

models = {
    "Ridge": Ridge(alpha=1.0),
    "Lasso": Lasso(alpha=0.1),
    "ElasticNet": ElasticNet(alpha=0.1, l1_ratio=0.5),
}

rows = []
for name, model in models.items():
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)
    mse = mean_squared_error(y_test, y_pred)
    r2 = r2_score(y_test, y_pred)
    rows.append({
        "Model": name,
        "MSE": round(mse, 3),
        "R2": round(r2, 3),
        "Non-zero": int(np.sum(model.coef_ != 0)),
    })

print(pd.DataFrame(rows).to_string(index=False))

Output:

text
     Model      MSE    R2  Non-zero
     Ridge 5638.100 0.705         6
     Lasso 5504.727 0.712         6
ElasticNet 5949.416 0.689         6

Kolom Non-zero pada output menunjukkan jumlah fitur yang dipertahankan oleh masing-masing model. Lasso biasanya menghasilkan nilai terkecil karena mendorong koefisien ke nol, sedangkan Ridge mempertahankan semuanya. Elastic Net berada di tengah, mempertahankan fitur yang relevan tanpa kehilangan stabilitas.

Menemukan alpha dan l1_ratio yang Optimal

Nilai alpha dan l1_ratio tidak bisa ditebak secara manual. alpha mengontrol kekuatan regularisasi secara keseluruhan, sedangkan l1_ratio mengontrol keseimbangan antara L1 dan L2. Kombinasi yang salah membuat model underfit atau overfit.

ElasticNetCV melakukan cross-validation secara otomatis untuk mencari kombinasi terbaik. Grid l1_ratio yang disarankan adalah [0.1, 0.5, 0.7, 0.9, 0.95, 0.99, 1.0], sementara jalur alpha dihitung otomatis oleh library. Setelah fitting, atribut alpha_ dan l1_ratio_ menyimpan nilai terbaik.

python
!pip install scikit-learn

from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import ElasticNetCV

X, y = make_regression(n_samples=300, n_features=8, noise=1.0, random_state=7)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

model = ElasticNetCV(
    l1_ratio=[0.1, 0.5, 0.7, 0.9, 0.95, 0.99, 1.0],
    cv=5,
    max_iter=10000,
    random_state=42,
)
model.fit(X_train, y_train)

print(f"alpha terbaik   : {model.alpha_:.4f}")
print(f"l1_ratio terbaik: {model.l1_ratio_:.2f}")
print(f"R2 pada data test: {model.score(X_test, y_test):.4f}")

Output:

text
alpha terbaik   : 0.1143
l1_ratio terbaik: 1.00
R2 pada data test: 1.0000

Interpretasi hasilnya bergantung pada nilai l1_ratio_. Nilai mendekati 1 berarti model berperilaku hampir seperti Lasso, sedangkan nilai mendekati 0 berarti model hampir seperti Ridge. Pada data dengan korelasi kuat, l1_ratio_ biasanya jatuh di tengah, yang menandakan campuran kedua penalty memang diperlukan. Nilai alpha_ yang besar menandakan regularisasi kuat, sementara nilai kecil menandakan model lebih mengandalkan data.

Validasi tambahan bisa dilakukan dengan membandingkan skor cross-validation pada grid l1_ratio yang berdekatan. Jika perbedaan skor antar nilai kecil, pilih nilai yang lebih sederhana untuk menghindari overfitting. Kita juga bisa memeriksa atribut mse_path_ untuk melihat seberapa stabil error antar fold pada setiap alpha. Pola error yang konsisten antar fold menandakan pilihan alpha yang robust terhadap variasi data.

Kapan Menggunakan Elastic Net di Proyek Nyata

Pilih Elastic Net saat fitur saling berkorelasi, saat p > n, atau saat kita butuh seleksi fitur sekaligus koefisien yang stabil. Ridge menang ketika semua fitur relevan dan tidak ada kebutuhan sparsity. Lasso menang ketika fitur benar-benar independen dan seleksi fitur menjadi prioritas utama.

Pitfall yang paling umum adalah melupakan standardisasi. Regularisasi sangat sensitif terhadap skala, sehingga fitur wajib di-standardize sebelum fitting. Tanpa langkah ini, fitur dengan skala besar mendominasi penalty dan hasilnya menyesatkan.

Saat monitoring, periksa stabilitas koefisien antar fold cross-validation. Koefisien yang berubah drastis antar fold menandakan model tidak stabil. Waspadai juga over-regularization, yaitu kondisi ketika semua koefisien mendekati nol dan model kehilangan informasi penting. Jika hal ini terjadi, turunkan nilai alpha dan evaluasi ulang metrik pada data validasi.

Best practice lain adalah menyimpan pipeline preprocessing dan model dalam satu objek, misalnya Pipeline, agar transformasi yang sama diterapkan saat inference. Dokumentasikan juga nilai alpha dan l1_ratio yang terpilih beserta metrik validasinya, supaya keputusan model bisa direproduksi oleh anggota tim lain. Pendekatan ini menjaga konsistensi antara eksperimen dan produksi.

Ringkasnya, Elastic Net adalah pilihan terbaik ketika dataset memiliki struktur korelasi yang kompleks. Model ini menggabungkan kekuatan L1 dan L2 tanpa harus memilih salah satu secara eksklusif.

Tertarik memperdalam Machine Learning dengan Python dan Scikit-learn? Bergabunglah dengan course dan bootcamp Rumah Coding untuk membangun fondasi yang solid, dari konsep regularisasi sampai deployment model nyata.

Artikel Terkait