Model regresi pertama kita juga model paling berpengaruh dalam sejarah statistika: regresi linier sederhana, prediksi dengan tepat satu fitur. Seluruh pengetahuannya muat dalam dua angka — kemiringan dan titik potong — sehingga kita bisa menghitungnya sendiri dengan rumus, lalu membuktikan bahwa API scikit-learn menghasilkan angka yang persis sama. Chapter ini membedah cara kerja least squares, arti tiap komponen garis, dan cara membaca koefisien coef_ serta intercept_. Setelah ini, melatih regresi tidak akan terasa seperti kotak hitam lagi: hanya aritmetika yang bisa kita lacak sampai ke angkanya.

Satu Fitur, Satu Garis #

Regresi linier sederhana memodelkan hubungan satu fitur x dengan target y sebagai garis lurus:

ŷ = w · x + b

di mana w adalah slope (kemiringan) dan b adalah intersep (titik potong sumbu tegak). Model “belajar” berarti mencari pasangan (w, b) yang paling pas dengan data latih. Untuk masalah harga rumah, w terbaca sebagai tambahan harga per satu meter persegi, dan b sebagai harga dasar ketika luas nol — angka yang secara teori memang tidak pernah terjadi di pasar, tetapi dibutuhkan garis untuk menentukan posisi vertikalnya.

Membaca garis hasil pelatihan sama mudahnya dengan membaca peta harga. Jika model menghasilkan harga = 7,51 × luas + 60,48, maka dua rumah yang berselisih 10 m² diprediksi berselisih 75,1 juta — informasi yang jauh lebih ringkas daripada menghafal dua belas transaksi mentah.

Intuisi Least Squares #

Bagaimana kita memilih garis terbaik dari tak terhingga kandidat? Kriteria standarnya: pilih garis yang membuat jumlah kuadrat jarak vertikal antara titik data dan garis sekecil mungkin. Jarak vertikal tiap titik — selisih y aktual dengan ŷ — disebut residu; least squares meminimalkan jumlah kuadrat residu. Ini persis fungsi loss MSE yang sudah kita hitung dengan tangan di Chapter Cara Machine Learning Belajar dari Data, dan di sini dipandang dari sisi geometrinya: garis yang dipilih adalah garis yang paling “dekat rata-rata” ke seluruh titik.

Untuk satu fitur, nilai terbaik (w, b) bahkan tidak perlu dicari bertahap — tersedia rumus tertutup:

w = Σ(xᵢ − x̄)(yᵢ − ȳ) / Σ(xᵢ − x̄)² (setara cov(x, y) / var(x))

b = ȳ − w · x̄

Pembilang mengukur seberapa bersama-sama x dan y bergerak dari nilai reratanya; penyebut mengukur seberapa lebar sebaran x. Rumus ini adalah hasil menurunkan loss terhadap kedua parameter lalu menyetel turunannya nol — perhitungan yang akan kita serahkan ke rumus, bukan diulang di sini.

Dari Rumus Manual ke API #

Sekarang bagian yang menyenangkan: menghitung kedua parameter dua kali — sekali dengan rumus di atas ditulis di NumPy, sekali lewat LinearRegression — lalu membandingkannya langsung. File regresi-sederhana.py juga memprediksi dua rumah baru dengan kedua cara dan menutup dengan R² yang dihitung manual serta lewat r2_score.

python
# Regresi linier sederhana: hitung garis terbaik dengan rumus manual,
# lalu bandingkan hasilnya dengan LinearRegression dari scikit-learn.

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score

# Dua belas rumah terjual di satu kompleks: luas (m^2) dan harga (juta rupiah).
luas = np.array([30.0, 35.0, 40.0, 45.0, 50.0, 55.0,
                 60.0, 65.0, 70.0, 75.0, 80.0, 85.0])
harga = np.array([292.0, 318.0, 365.0, 390.0, 441.0, 470.0,
                  516.0, 543.0, 590.0, 615.0, 668.0, 700.0])

# Bagian 1 -- rumus least squares manual:
# slope = jumlah((x - x_bar) * (y - y_bar)) / jumlah((x - x_bar)^2)
# intersep = y_bar - slope * x_bar
x_mid = luas - luas.mean()
y_mid = harga - harga.mean()
slope_manual = np.sum(x_mid * y_mid) / np.sum(x_mid**2)
intersep_manual = harga.mean() - slope_manual * luas.mean()

# Bagian 2 -- API scikit-learn mempelajari garis yang sama lewat fit().
model = LinearRegression()
model.fit(luas.reshape(-1, 1), harga)
slope_api = model.coef_[0]
intersep_api = model.intercept_

print("Perbandingan rumus manual vs LinearRegression:")
print(f"{'besaran':<10} {'manual':>12} {'scikit-learn':>14}")
print(f"{'slope':<10} {slope_manual:>12.4f} {slope_api:>14.4f}")
print(f"{'intersep':<10} {intersep_manual:>12.4f} {intersep_api:>14.4f}")
print()

# Prediksi dua rumah baru: hitung manual dengan rumus garis,
# lalu bandingkan dengan model.predict().
for l_baru in [48.0, 82.0]:
    p_manual = slope_manual * l_baru + intersep_manual
    p_api = model.predict(np.array([[l_baru]]))[0]
    print(f"luas {l_baru:4.0f} m^2 -> manual {p_manual:7.2f} juta | "
          f"predict() {p_api:7.2f} juta")
print()

# Bagian 3 -- R^2 manual (1 - SSE/SST) vs r2_score dari scikit-learn.
prediksi_latih = slope_api * luas + intersep_api
sse = np.sum((harga - prediksi_latih) ** 2)
sst = np.sum((harga - harga.mean()) ** 2)
r2_manual = 1.0 - sse / sst
r2_api = r2_score(harga, prediksi_latih)
print(f"R^2 manual  = 1 - SSE/SST = {r2_manual:.4f}")
print(f"R^2 r2_score             = {r2_api:.4f}")

Output yang Dihasilkan:

text
Perbandingan rumus manual vs LinearRegression:
besaran          manual   scikit-learn
slope            7.5105         7.5105
intersep        60.4802        60.4802

luas   48 m^2 -> manual  420.98 juta | predict()  420.98 juta
luas   82 m^2 -> manual  676.34 juta | predict()  676.34 juta

R^2 manual  = 1 - SSE/SST = 0.9981
R^2 r2_score             = 0.9981

Bedah kode di atas baris demi baris:

  1. Baris 9–12 (luas, harga): dua belas titik data ditulis tetap — satu fitur numerik dan satu target kontinu, syarat lengkap regresi sederhana.
  2. Baris 17–20 (rumus manual): x_mid dan y_mid adalah selisih tiap nilai terhadap reratanya. Pembilang np.sum(x_mid * y_mid) menjumlahkan hasil kali simpangan; penyebut np.sum(x_mid**2) menjumlahkan kuadrat simpangan x. Keduanya menghasilkan slope 7,5105; intersep tinggal 60,4802 dari ȳ − w·x̄.
  3. Baris 23–26 (API): fit() menjalankan least squares yang sama di balik layar, lalu menyimpan hasilnya pada dua atribut: coef_ berisi array slope (satu nilai per fitur) dan intercept_ berisi skalar b.
  4. Baris 28–31 (tabel perbandingan): keempat angka dicetak berdampingan — kolom manual dan kolom scikit-learn identik sampai desimal keempat. Bukan kebetulan: keduanya menyelesaikan optimasi yang sama secara eksak.
  5. Baris 36–40 (prediksi ganda): rumus garis dievaluasi dengan tangan untuk luas 48 dan 82 m², lalu dibandingkan dengan model.predict(). Hasilnya sama persis: 420,98 juta dan 676,34 juta.
  6. Baris 44–50 (R² dua versi): residu dijumlahkan kuadratnya menjadi SSE, sebaran total terhadap rerata menjadi SST, dan 1 − SSE/SST menghasilkan 0,9981 — sama dengan r2_score.

Membaca Slope, Intersep, dan R² #

Ketiga besaran hasil pelatihan masing-masing punya bacaan praktis:

BesaranNilai contohCara membaca
coef_ (slope)7,5105Tiap tambahan 1 m² luas menaikkan prediksi harga sekitar 7,51 juta
intercept_60,4802Titik potong garis di luas 0 — harga dasar teoretis, bukan harga pasar nyata
R²0,998199,81% variasi harga data latih terjelaskan oleh luas lewat garis ini

R² perlu dibaca dengan kepala dingin: nilainya dihitung pada data yang sama dengan data latih, dan “variasi terjelaskan” bukan jaminan hubungan sebab-akibat. Data harga rumah memang hampir selalu bergerak seiring luas, sehingga R² tinggi wajar di sini; pada data yang lebih berderau, angka 0,3 saja bisa jadi kabar baik. Kita akan membedah R² bersama MAE, MSE, dan RMSE secara menyeluruh di Chapter Metrik Evaluasi Regresi.

Satu fitur memang jarang cukup di dunia nyata: harga rumah dipengaruhi luas sekaligus jarak ke pusat kota, jumlah kamar, dan usia bangunan. Kabar baiknya, seluruh mesin yang baru kita bangun — least squares, coef_, intercept_ — naik kelas ke banyak fitur tanpa perlu rancang ulang: rumusnya menjadi perkalian matriks, dan tiap fitur mendapat koefisien sendiri. Perluasan itulah isi Chapter Regresi Linier Berganda.

Referensi Resmi #