DataLens

Overfitting, underfitting, dan generalisasi

Lihat bagaimana model bisa terlalu sederhana atau terlalu fleksibel, ukur dengan error training vs test, dan pelajari kebiasaan yang menjaga model tetap jujur.

Level: Dasar · 7 menit baca

Latihan modul ini

Soal-soal untuk pelajaran ini berjalan di browser-mu dan langsung mengecek jawabanmu.

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:

Analogi

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:

Dengan 20 titik training, polinomial derajat 15 punya cukup fleksibilitas untuk hampir melewati setiap titik. Ia "mempelajari" noise-nya.

Ide utama

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 trainingError testPerbaikan
Underfittingtinggitinggimodel yang lebih fleksibel, fitur yang lebih baik
Cocok dengan baikrendahrendah, dekat dengan trainingpertahankan
Overfittingsangat rendahjauh lebih tinggimodel 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

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

Latihan modul ini

Soal-soal untuk pelajaran ini berjalan di browser-mu dan langsung mengecek jawabanmu.

Berikutnya: Regresi linear (intuisi matematikanya) →