Model regresi memprediksi angka kontinu — harga rumah, suhu, durasi pengiriman — sehingga galatnya tidak lagi bisa dihitung sebagai “benar atau salah”. Sebesar apa pun galatnya, dan bagaimana kita menghukum galat besar, adalah dua keputusan yang dibakukan di dalam pilihan metrik. Pada chapter ini kita menghitung MAE, MSE, RMSE, dan R² secara manual selangkah demi selangkah pada sepuluh prediksi harga rumah, memverifikasi hasilnya terhadap scikit-learn 1.5.2, lalu menyisipkan satu pencilan untuk menyaksikan betapa berbedanya reaksi tiap metrik. Bagian penutup menghubungkan fungsi loss yang dioptimasi saat pelatihan dengan metrik yang kita laporkan saat evaluasi — dua hal yang sering dianggap sama padahal merupakan pilihan terpisah.
Tiga Cara Mengukur Galat #
Misalkan galat = aktual − prediksi untuk tiap sampel. Tiga metrik pertama hanya berbeda pada cara merangkum daftar galat tersebut. MAE (mean absolute error) merata-rata nilai mutlak galat, MSE (mean squared error) merata-rata kuadratnya, dan RMSE (root mean squared error) mengembalikan MSE ke satuan asli lewat akar kuadrat. Konsekuensi kuadrat bersifat ganda: galat 2 satuan dihukum 4, galat 6 dihukum 36 — dan satuan MSE menjadi kuadrat satuan target (ratus juta rupiah kuadrat), yang tidak punya makna bisnis langsung.
R² (koefisien determinasi) membaca galat secara relatif terhadap pembanding paling sederhana: prediksi rata-rata. Rumusnya 1 − SS_res / SS_tot, dengan SS_res jumlah kuadrat galat model dan SS_tot jumlah kuadrat galat model rata-rata. R² bernilai 1 saat model sempurna, 0 saat kinerjanya sama dengan menebak rata-rata, dan bisa negatif saat model lebih buruk daripada rata-rata.
Eksperimen: Manual, Verifikasi, Lalu Disengat Pencilan #
Berkas metrik_regresi.py terbagi tiga bagian: sepuluh prediksi harga rumah yang dihitung manual lalu diverifikasi, penyisipan satu pencilan pada prediksi ke-4, dan pelatihan LinearRegression pada data sintetis untuk melihat hubungan loss pelatihan dengan metrik evaluasi:
"""MAE, MSE, RMSE, dan R-kuadrat: hitung manual, verifikasi sklearn,
lalu ukur kepekaan tiap metrik terhadap pencilan.
Bagian 1: prediksi harga rumah 10 sampel (satuan ratus juta rupiah),
semua metrik dihitung manual selangkah demi selangkah lalu diverifikasi
terhadap scikit-learn.
Bagian 2: satu pencilan disisipkan pada satu prediksi -> bandingkan
pergeseran tiap metrik.
Bagian 3: LinearRegression pada data sintetis -> hubungan loss
pelatihan (MSE) dengan metrik evaluasi pada data uji.
"""
import numpy as np
from sklearn.datasets import make_regression
from sklearn.linear_model import LinearRegression
from sklearn.metrics import (
mean_absolute_error,
mean_squared_error,
r2_score,
root_mean_squared_error,
)
from sklearn.model_selection import train_test_split
# ---------- 1. Sepuluh prediksi harga rumah, tanpa pencilan ----------
harga_aktual = np.array(
[3.2, 4.8, 2.5, 6.1, 5.4, 3.9, 7.2, 4.1, 5.8, 3.5]
)
harga_prediksi = np.array(
[3.1, 5.0, 2.6, 5.9, 5.7, 4.1, 7.1, 4.4, 5.7, 3.7]
)
def metrik_manual(aktual, prediksi, label):
galat = aktual - prediksi
print(f"-- {label} --")
print("galat per sampel:", np.round(galat, 2))
mae = float(np.mean(np.abs(galat)))
mse = float(np.mean(galat ** 2))
rmse = float(np.sqrt(mse))
ss_res = float(np.sum(galat ** 2))
ss_tot = float(np.sum((aktual - aktual.mean()) ** 2))
r2 = 1.0 - ss_res / ss_tot
print(f"MAE manual = {mae:.4f} | MSE manual = {mse:.4f} | "
f"RMSE manual = {rmse:.4f} | R2 manual = {r2:.4f}")
return mae, mse, rmse, r2
def verifikasi_sklearn(aktual, prediksi):
return (
mean_absolute_error(aktual, prediksi),
mean_squared_error(aktual, prediksi),
root_mean_squared_error(aktual, prediksi),
r2_score(aktual, prediksi),
)
print("== bagian 1: sepuluh prediksi wajar ==")
(mae_a, mse_a, rmse_a, r2_a) = metrik_manual(
harga_aktual, harga_prediksi, "hitung manual"
)
skl = verifikasi_sklearn(harga_aktual, harga_prediksi)
print(f"sklearn : MAE={skl[0]:.4f} MSE={skl[1]:.4f} "
f"RMSE={skl[2]:.4f} R2={skl[3]:.4f}")
print("manual identik dengan sklearn:",
bool(np.allclose((mae_a, mse_a, rmse_a, r2_a), skl)))
# ---------- 2. Satu pencilan: prediksi ke-4 melenceng jauh ----------
print("\n== bagian 2: satu pencilan pada sampel ke-4 (+6.0 ratus juta) ==")
harga_pencilan = harga_prediksi.copy()
harga_pencilan[3] += 6.0 # misal: fitur luas tanah keliru satu digit
(mae_b, mse_b, rmse_b, r2_b) = metrik_manual(
harga_aktual, harga_pencilan, "hitung manual dengan pencilan"
)
skl_b = verifikasi_sklearn(harga_aktual, harga_pencilan)
print(f"sklearn : MAE={skl_b[0]:.4f} MSE={skl_b[1]:.4f} "
f"RMSE={skl_b[2]:.4f} R2={skl_b[3]:.4f}")
print("\npergeseran metrik akibat satu pencilan:")
print(f"{'metrik':6s} | {'tanpa pencilan':>14s} | {'dengan pencilan':>15s} | "
f"{'pergeseran':>10s} | {'naik berapa kali':>16s}")
for nama, a, b in (
("MAE", mae_a, mae_b),
("MSE", mse_a, mse_b),
("RMSE", rmse_a, rmse_b),
):
print(f"{nama:6s} | {a:14.4f} | {b:15.4f} | {b - a:10.4f} | "
f"{b / a:16.1f}x")
print(f"{'R2':6s} | {r2_a:14.4f} | {r2_b:15.4f} | {r2_b - r2_a:10.4f} | "
f"{'turun, bukan naik':>16s}")
# ---------- 3. Loss pelatihan vs metrik evaluasi ----------
print("\n== bagian 3: LinearRegression pada data sintetis ==")
X, y = make_regression(
n_samples=120, n_features=3, noise=12.0, random_state=42
)
X_latih, X_uji, y_latih, y_uji = train_test_split(
X, y, test_size=0.25, random_state=42
)
model = LinearRegression()
model.fit(X_latih, y_latih)
pred_latih = model.predict(X_latih)
pred_uji = model.predict(X_uji)
mse_latih = mean_squared_error(y_latih, pred_latih)
print(f"MSE data latih (fungsi tujuan optimizer) : {mse_latih:.4f}")
print(f"MSE data uji (metrik evaluasi) : "
f"{mean_squared_error(y_uji, pred_uji):.4f}")
print(f"RMSE data uji (akar, satu satuan y) : "
f"{root_mean_squared_error(y_uji, pred_uji):.4f}")
print(f"MAE data uji (satu satuan y) : "
f"{mean_absolute_error(y_uji, pred_uji):.4f}")
print(f"R2 data uji : "
f"{r2_score(y_uji, pred_uji):.4f}")Skrip di atas menghasilkan keluaran berikut saat dijalankan di mesin uji. Fokuskan perhatian pada kolom “naik berapa kali” di bagian kedua.
Output yang Dihasilkan:
== bagian 1: sepuluh prediksi wajar ==
-- hitung manual --
galat per sampel: [ 0.1 -0.2 -0.1 0.2 -0.3 -0.2 0.1 -0.3 0.1 -0.2]
MAE manual = 0.1800 | MSE manual = 0.0380 | RMSE manual = 0.1949 | R2 manual = 0.9804
sklearn : MAE=0.1800 MSE=0.0380 RMSE=0.1949 R2=0.9804
manual identik dengan sklearn: True
== bagian 2: satu pencilan pada sampel ke-4 (+6.0 ratus juta) ==
-- hitung manual dengan pencilan --
galat per sampel: [ 0.1 -0.2 -0.1 -5.8 -0.3 -0.2 0.1 -0.3 0.1 -0.2]
MAE manual = 0.7400 | MSE manual = 3.3980 | RMSE manual = 1.8434 | R2 manual = -0.7493
sklearn : MAE=0.7400 MSE=3.3980 RMSE=1.8434 R2=-0.7493
pergeseran metrik akibat satu pencilan:
metrik | tanpa pencilan | dengan pencilan | pergeseran | naik berapa kali
MAE | 0.1800 | 0.7400 | 0.5600 | 4.1x
MSE | 0.0380 | 3.3980 | 3.3600 | 89.4x
RMSE | 0.1949 | 1.8434 | 1.6484 | 9.5x
R2 | 0.9804 | -0.7493 | -1.7297 | turun, bukan naik
== bagian 3: LinearRegression pada data sintetis ==
MSE data latih (fungsi tujuan optimizer) : 147.6056
MSE data uji (metrik evaluasi) : 155.4169
RMSE data uji (akar, satu satuan y) : 12.4666
MAE data uji (satu satuan y) : 9.0854
R2 data uji : 0.9843Mari kita telaah kode di atas baris per baris:
- Baris 24-30 (data): sepuluh pasangan harga aktual-prediksi dalam satuan ratus juta rupiah; seluruh galatnya kecil, antara −0,3 hingga 0,3.
- Baris 33-45 (
metrik_manual): satu fungsi menghitung keempat metrik dari definisi — rata-rata mutlak, rata-rata kuadrat, akarnya, dan1 − SS_res / SS_tot— sekaligus mencetak galat per sampel agar tiap angka bisa ditelusuri. - Baris 48-53 (
verifikasi_sklearn): keempat fungsi sklearn (mean_absolute_error,mean_squared_error,root_mean_squared_error,r2_score) dipanggil pada input yang sama. - Baris 61-65 (verifikasi):
np.allclosemembandingkan hasil manual dengan sklearn — baris ini bukti bahwa rumus di atas memang persis yang dijalankan pustaka. - Baris 69-70 (pencilan): satu prediksi dinaikkan 6,0 — setara salah input satu digit pada fitur luas tanah — sisanya tidak disentuh.
- Baris 78-88 (tabel pergeseran): tiap metrik dibandingkan sebelum dan sesudah pencilan, termasuk faktor kali kenaikannya.
- Baris 91-113 (bagian 3):
make_regressionmembuat data sintetis dengan relasi linear dan noise 12,0; model dilatih pada 90 sampel dan dievaluasi pada 30 sampel uji.
Membaca Hasil: Satu Pencilan, Tiga Reaksi #
Tabel pergeseran adalah inti demonstrasi ini. MAE naik dari 0,1800 ke 0,7400 — sekitar 4,1 kali — karena satu galat 5,8 hanya menyumbang 0,58 pada rata-rata. MSE melonjak dari 0,0380 ke 3,3980, sekitar 89,4 kali, karena kuadrat galat 5,8 adalah 33,64 dan menguasai total. RMSE berada di antara keduanya (9,5 kali) sekaligus tetap terbaca dalam satuan target: 1,8434 ratus juta rupiah. R² anjlok dari 0,9804 ke −0,7493 — model dengan satu prediksi menyimpang itu kini lebih buruk daripada model yang cuma menebak rata-rata pasar.
Pilihan metrik dengan demikian merupakan pernyataan kebijakan. Tabel berikut merangkumnya:
| Metrik | Rumus | Kepekaan pencilan | Kapan dipakai |
|---|---|---|---|
| MAE | rata-rata |galat| | rendah — semua galat berbobot linear | galat besar wajar terjadi dan ingin dilaporkan apa adanya |
| MSE | rata-rata galat² | tinggi — galat besar dihukum kuadrat | fungsi loss pelatihan; menuntut galat besar ditindak tegas |
| RMSE | √MSE | tinggi, tetapi satuan kembali ke target | pelaporan ketika outlier penting namun angka harus terbaca bisnis |
| R² | 1 − SS_res/SS_tot | tinggi — bisa berubah tanda | membandingkan model terhadap baseline rata-rata |
Loss Pelatihan Bukan Metrik Laporan #
Bagian ketiga memperlihatkan hubungan yang sering kabur. LinearRegression meminimalkan MSE pada data latih — angka 147,6056 pada keluaran adalah nilai fungsi tujuan optimizer, bukan klaim kualitas. Pada data uji, MSE naik ke 155,4169, sementara RMSE 12,4666 dan MAE 9,0854 sudah berada dalam satuan target sehingga langsung bisa dibandingkan dengan toleransi bisnis. R² 0,9843 membandingkan model terhadap baseline rata-rata pada data uji.
Kita kini mampu mengukur kedua keluarga model — klasifikasi maupun regresi — dengan metrik yang sesuai. Angka-angka itu pun akan jauh lebih bermakna ketika kita tahu kesalahan tersebar di mana: pasangan kelas mana yang saling tertukar, dan seberapa sering. Chapter berikutnya, Confusion Matrix , membedah tabel dua dimensi yang menjawab pertanyaan tersebut baris demi baris.