Idenya
Tujuan sebuah model bukan untuk cocok dengan data training. Tujuannya adalah generalisasi: membuat prediksi yang baik pada data baru yang belum pernah dilihatnya. Dua hal bisa salah:
- Underfitting: model terlalu sederhana untuk menangkap pola yang sebenarnya. Ia buruk di data training dan di data baru.
- Overfitting: model begitu fleksibel sehingga menghafal data training, termasuk noise-nya. Ia sangat bagus di data training dan buruk di data baru.
Belajar untuk ujian. Underfitting adalah membaca sekilas ringkasannya dan melewatkan ide-idenya. Overfitting adalah menghafal kata demi kata jawaban tahun lalu: sempurna untuk soal-soal persis itu, bingung di soal baru. Generalisasi adalah memahami materinya.
Sebuah eksperimen
Ambil sampel kecil hari: belanja iklan vs unit kopi, yang punya hubungan naik yang nyata tapi penuh noise. Cocokkan polinomial dengan derajat yang semakin tinggi (polinomial derajat 1 adalah garis lurus; derajat yang lebih tinggi bisa lebih melengkung) lalu bandingkan error training dan test.
import numpy as np
import pandas as pd
demand = pd.read_csv("daily_demand.csv")
rng = np.random.default_rng(19)
sample = demand.iloc[rng.choice(len(demand), size=30, replace=False)]
train, test = sample.iloc[:20], sample.iloc[20:]
def fit_and_score(degree):
coefs = np.polyfit(train["ad_spend"], train["units"], degree)
train_mse = np.mean((np.polyval(coefs, train["ad_spend"]) - train["units"]) ** 2)
test_mse = np.mean((np.polyval(coefs, test["ad_spend"]) - test["units"]) ** 2)
return float(round(train_mse, 1)), float(round(test_mse, 1))
for degree in [0, 1, 3, 9, 15]:
print(degree, fit_and_score(degree))
0 (138.8, 170.5)
1 (85.7, 94.2)
3 (84.0, 110.8)
9 (31.0, 531.1)
15 (3.8, 9636.3)
(Kamu mungkin juga melihat RankWarning: NumPy memperingatkan bahwa polinomial derajat 15 pada 20 titik itu goyah secara numerik. Itulah intinya.)
Baca tabelnya dari atas ke bawah:
- Derajat 0 (garis datar: "selalu prediksi rata-ratanya") underfit: kedua error-nya tinggi.
- Derajat 1 menangkap tren yang sebenarnya; error training dan test-nya mirip.
- Saat derajatnya naik, error training terus turun, karena kurvanya bisa meliuk melewati lebih banyak titik. Tapi error test meledak: liukan-liukan itu dicocokkan ke noise, dan berayun liar di antara titik-titik training.
Dengan 20 titik training, polinomial derajat 15 punya cukup fleksibilitas untuk hampir melewati setiap titik. Ia "mempelajari" noise-nya.
Error training selalu membaik dengan fleksibilitas yang lebih besar; dengan sendirinya ia tidak berguna sebagai panduan kualitas model. Nilai model berdasarkan error-nya pada data yang tidak dipakai untuk melatihnya.
Dua gejalanya
| Error training | Error test | Perbaikan | |
|---|---|---|---|
| Underfitting | tinggi | tinggi | model yang lebih fleksibel, fitur yang lebih baik |
| Cocok dengan baik | rendah | rendah, dekat dengan training | pertahankan |
| Overfitting | sangat rendah | jauh lebih tinggi | model yang lebih sederhana, lebih banyak data, regularisasi |
Selisih antara error training dan test adalah ciri khas overfitting.
Menghafal: kasus ekstremnya
"Model" yang hanya menyimpan data training di sebuah dictionary punya error training nol:
lookup = dict(zip(train["date"], train["units"]))
train_errors = [lookup[d] - u for d, u in zip(train["date"], train["units"])]
print("training MSE:", np.mean(np.square(train_errors)))
print("can predict a new date?", test["date"].iloc[0] in lookup)
training MSE: 0.0
can predict a new date? False
Sempurna untuk masa lalu, tidak berguna untuk masa depan. Setiap model yang overfit adalah versi lebih halus dari tabel pencarian ini.
Apa yang membantu generalisasi
- Lebih banyak data. Semakin banyak contoh, semakin sulit menghafal noise, sehingga model yang fleksibel menjadi lebih aman.
- Model yang lebih sederhana. Utamakan model paling sederhana yang menangkap polanya. Mulai sederhana, tambahkan kerumitan hanya saat error validasi membaik.
- Fitur yang baik. Satu fitur yang informatif (hari sejak order terakhir) mengalahkan sepuluh fitur yang penuh noise.
- Regularisasi. Hukum kerumitan selama training (modul selanjutnya).
- Evaluasi yang jujur. Set validasi terpisah atau cross-validation untuk memilih model, dan set test yang disentuh sekali di akhir (modul setelah berikutnya).
Pengecekan cepat yang selalu bisa kamu lakukan
Bagaimana kamu tahu apakah model buatanmu overfitting? Bandingkan seberapa salah model itu pada data yang dipakai untuk melatihnya dengan seberapa salah pada data yang belum pernah dilihatnya. Ini pengecekan itu untuk model yang memprediksi unit terjual harian dari cuaca, hari dalam seminggu, promosi, dan biaya iklan:
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
X = demand[["temperature", "weekend", "promo", "ad_spend"]]
y = demand["units"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
model = LinearRegression().fit(X_train, y_train)
train_mse = np.mean((model.predict(X_train) - y_train) ** 2)
test_mse = np.mean((model.predict(X_test) - y_test) ** 2)
print(round(train_mse, 1), round(test_mse, 1))
23.4 29.4
Error training dan test-nya dekat: model linear sederhana ini bergeneralisasi dengan baik. Kalau error test-nya beberapa kali lipat error training, kamu punya masalah overfitting yang harus diperbaiki sebelum memercayainya.
Mengapa ini penting untuk kode yang bersih
Selalu melaporkan performa training dan test adalah padanan ML dari menulis test: ia menangkap bug (overfitting) yang terlihat seperti keberhasilan. Masukkan perbandingan itu ke alur kerjamu (fungsi kecil evaluate(model) yang mencetak keduanya), dan kamu tidak akan pernah merilis model yang hanya berfungsi pada data kemarin.
Rangkuman
- Tujuannya adalah generalisasi: prediksi yang baik pada data yang belum dilihat.
- Underfitting: terlalu sederhana, buruk di mana-mana. Overfitting: terlalu fleksibel, bagus di data training, buruk di data baru.
- Error training selalu turun dengan fleksibilitas; nilai model berdasarkan data yang disisihkan.
- Selisih besar antara error training dan test menandakan overfitting.
- Lebih banyak data, model yang lebih sederhana, fitur yang lebih baik, regularisasi dan evaluasi yang jujur semuanya membantu.