Regresi logistik adalah salah satu algoritma fundamental dalam machine learning yang digunakan untuk tugas klasifikasi biner. Meskipun banyak library seperti Scikit-learn menyediakan implementasi siap pakai, memahami cara kerja algoritma ini dari dasar sangatlah berharga. Tutorial ini akan memandu Anda langkah demi langkah untuk membangun model regresi logistik dari awal (from scratch) hanya menggunakan Python dan library NumPy, dengan fokus pada pemahaman matematis di balik perhitungan gradien dan pembaruan bobot.
Regresi logistik, meskipun namanya mengandung "regresi", sebenarnya adalah algoritma klasifikasi. Tujuan utamanya adalah untuk memodelkan probabilitas sebuah instance data termasuk dalam kelas tertentu (biasanya kelas '1' dalam kasus biner). Algoritma ini menggunakan fungsi logistik (atau sigmoid) untuk memetakan output dari kombinasi linear fitur ke dalam rentang probabilitas [0, 1]. Mempelajari implementasi regresi logistik python from scratch memberikan pemahaman mendalam tentang bagaimana model belajar dari data, bagaimana optimasi bekerja, dan apa yang terjadi di balik layar fungsi-fungsi library tingkat tinggi. Tutorial ini secara khusus akan berfokus pada penggunaan NumPy untuk operasi matematis dan vektorisasi yang efisien, menjadikannya contoh bagus dari machine learning from scratch python tanpa bergantung pada library seperti Scikit-learn (regresi logistik python tanpa scikit learn).
Prasyarat untuk Mengikuti Tutorial Ini
Sebelum memulai, pastikan Anda memiliki pemahaman dasar tentang:
- Pemrograman dasar Python (variabel, fungsi, loop, struktur data).
- Dasar-dasar NumPy (membuat array, operasi array, slicing, broadcasting, konsep vektorisasi).
- Konsep dasar Machine Learning (apa itu dataset, fitur, label/target, training/pelatihan model, bobot/parameter).
- (Opsional tapi membantu) Pemahaman dasar kalkulus, terutama konsep turunan/derivatif, yang penting untuk memahami gradient descent.
Memahami Konsep Inti di Balik Regresi Logistik
Tiga komponen utama membentuk inti dari regresi logistik: fungsi sigmoid, fungsi biaya, dan algoritma optimasi (gradient descent).
Fungsi Sigmoid dan Implementasinya di Python NumPy
Fungsi sigmoid (atau fungsi logistik) adalah kunci yang mengubah output linear dari kombinasi bobot dan fitur menjadi probabilitas. Ia memetakan nilai input apa pun ke rentang antara 0 dan 1.
Rumus matematis fungsi sigmoid adalah:
σ(z) = 1 / (1 + e^(-z))
Di mana z
adalah output linear (biasanya z = X @ weights
, hasil perkalian matriks fitur dengan bobot). Implementasi menggunakan NumPy sangatlah efisien:
import numpy as np
def sigmoid(z):
"""Menghitung fungsi sigmoid.
Args:
z: Input skalar atau array NumPy.
Returns:
Output sigmoid, array NumPy dengan shape yang sama seperti z.
"""
return 1 / (1 + np.exp(-z))
Transisi ke fungsi berikutnya, fungsi biaya, sangat penting karena ini yang akan kita minimalkan.
Fungsi Biaya Binary Cross-Entropy (Log Loss)
Untuk melatih model, kita perlu cara mengukur seberapa baik prediksinya dibandingkan dengan label sebenarnya. Akurasi saja tidak cukup karena tidak memberikan sinyal gradien yang halus untuk optimasi. Untuk masalah klasifikasi biner seperti regresi logistik, fungsi biaya yang umum digunakan adalah Binary Cross-Entropy atau Log Loss.
Fungsi ini memberikan penalti yang besar jika model memprediksi probabilitas yang jauh dari label sebenarnya (misalnya, memprediksi probabilitas rendah untuk kelas 1 padahal label sebenarnya 1).
Rumus matematis Binary Cross-Entropy (J) adalah:
J(weights) = - (1/m) * Σ [ y * log(h) + (1 - y) * log(1 - h) ]
Di mana:
m
adalah jumlah sampel data.y
adalah label sebenarnya (0 atau 1).h
adalah prediksi probabilitas dari model (output dari fungsi sigmoid).Σ
menunjukkan penjumlahan atas semua sampel data.
Implementasinya dengan NumPy juga memanfaatkan vektorisasi. Setelah mengetahui cara mengukur kesalahan, langkah berikutnya adalah memperbaikinya.
Optimasi dengan Gradient Descent Menggunakan NumPy
Tujuan pelatihan adalah menemukan set bobot (weights) yang meminimalkan fungsi biaya (J). Gradient Descent adalah algoritma optimasi iteratif yang umum digunakan untuk tujuan ini. Cara kerjanya adalah dengan menghitung gradien (turunan parsial) dari fungsi biaya terhadap setiap bobot, lalu memperbarui bobot ke arah yang berlawanan dengan gradien (menuruni 'lembah' fungsi biaya).
Langkah-langkah utamanya:
- Inisialisasi bobot (misalnya, dengan nol atau nilai acak kecil).
- Hitung prediksi probabilitas (
h
) menggunakan bobot saat ini dan fungsi sigmoid. - Hitung gradien (turunan) dari fungsi biaya J terhadap bobot (
weights
). Rumus gradien untuk regresi logistik (vektorisasi) adalah:
Di mana:gradient = (1 / m) * X.T @ (h - y)
X
adalah matriks fitur (dengan kolom bias jika digunakan).X.T
adalah transpose dari matriks X.h
adalah vektor prediksi probabilitas.y
adalah vektor label sebenarnya.@
adalah operator perkalian matriks NumPy.
- Perbarui bobot menggunakan learning rate (
alpha
):weights = weights - alpha * gradient
- Ulangi langkah 2-4 untuk sejumlah iterasi tertentu atau hingga konvergensi.
NumPy sangat membantu di sini karena memungkinkan kita menghitung prediksi h
dan gradien untuk semua sampel data sekaligus menggunakan operasi matriks yang teroptimasi, daripada menggunakan loop Python eksplisit yang lebih lambat. Dengan pemahaman konsep ini, kita siap untuk implementasi kode.
Langkah Implementasi Regresi Logistik Python & NumPy
Mari kita gabungkan konsep-konsep di atas ke dalam kode Python.
Langkah 1: Persiapan Data
Untuk tutorial ini, kita akan membuat dataset sintetis sederhana menggunakan NumPy. Kita akan membuat dua kelompok titik data 2D yang kira-kira dapat dipisahkan secara linear.
import numpy as np
import matplotlib.pyplot as plt # Untuk visualisasi nanti (opsional)
# Membuat dataset sintetis
np.random.seed(0) # Untuk reproduktifitas
m = 100 # Jumlah sampel
# Fitur untuk kelas 0
X0 = np.random.randn(m // 2, 2) + np.array([1, 1])
y0 = np.zeros((m // 2, 1))
# Fitur untuk kelas 1
X1 = np.random.randn(m // 2, 2) + np.array([4, 4])
y1 = np.ones((m // 2, 1))
# Menggabungkan data
X = np.vstack((X0, X1))
y = np.vstack((y0, y1))
# Menambahkan kolom bias (kolom berisi angka 1) ke matriks fitur X
# Ini memungkinkan model mempelajari intercept (bias term)
X_b = np.c_[np.ones((m, 1)), X] # X_b sekarang berukuran (m, n+1) dimana n=jumlah fitur
print("Shape X_b:", X_b.shape)
print("Shape y:", y.shape)
# Opsional: Visualisasi data awal
# plt.figure(figsize=(8, 6))
# plt.scatter(X[y.flatten() == 0][:, 0], X[y.flatten() == 0][:, 1], color='blue', label='Kelas 0')
# plt.scatter(X[y.flatten() == 1][:, 0], X[y.flatten() == 1][:, 1], color='red', label='Kelas 1')
# plt.xlabel("Fitur 1")
# plt.ylabel("Fitur 2")
# plt.title("Dataset Sintetis")
# plt.legend()
# plt.show()
Setelah data siap, kita lanjutkan ke implementasi fungsi-fungsi inti.
Langkah 2: Implementasi Fungsi Sigmoid NumPy
Kita sudah mendefinisikan ini sebelumnya, mari kita sertakan lagi untuk kelengkapan.
def sigmoid(z):
"""Menghitung fungsi sigmoid."""
# Mencegah overflow untuk nilai z negatif yang sangat besar
z = np.clip(z, -500, 500)
return 1 / (1 + np.exp(-z))
Selanjutnya, kita implementasikan fungsi untuk menghitung biaya.
Langkah 3: Implementasi Fungsi Biaya NumPy
Fungsi ini menghitung rata-rata log loss pada seluruh dataset.
def compute_cost(X, y, weights):
"""Menghitung biaya (Binary Cross-Entropy).
Args:
X: Matriks fitur (termasuk kolom bias), shape (m, n+1).
y: Vektor label sebenarnya, shape (m, 1).
weights: Vektor bobot, shape (n+1, 1).
Returns:
Nilai biaya (skalar).
"""
m = len(y)
z = X @ weights # Output linear (m, 1)
h = sigmoid(z) # Prediksi probabilitas (m, 1)
# Menghindari log(0) atau log(1) dengan clipping kecil
epsilon = 1e-5
cost = - (1 / m) * np.sum(y * np.log(h + epsilon) + (1 - y) * np.log(1 - h + epsilon))
return cost
Dengan fungsi biaya siap, kita bisa melangkah ke proses optimasi.
Langkah 4: Implementasi Gradient Descent NumPy
Ini adalah inti dari proses pelatihan. Fungsi ini akan melakukan iterasi untuk memperbarui bobot.
def gradient_descent(X, y, weights, learning_rate, iterations):
"""Melakukan gradient descent untuk menemukan bobot optimal.
Args:
X: Matriks fitur (termasuk kolom bias), shape (m, n+1).
y: Vektor label sebenarnya, shape (m, 1).
weights: Vektor bobot awal, shape (n+1, 1).
learning_rate: Tingkat pembelajaran (alpha).
iterations: Jumlah iterasi.
Returns:
weights_final: Bobot optimal setelah pelatihan.
cost_history: List berisi nilai biaya pada setiap iterasi.
"""
m = len(y)
cost_history = []
for i in range(iterations):
# 1. Hitung prediksi
z = X @ weights
h = sigmoid(z) # (m, 1)
# 2. Hitung gradien
gradient = (1 / m) * (X.T @ (h - y)) # (n+1, 1)
# 3. Update bobot
weights = weights - learning_rate * gradient
# 4. Catat biaya (opsional, untuk monitoring)
cost = compute_cost(X, y, weights)
cost_history.append(cost)
# Cetak biaya setiap beberapa iterasi (opsional)
if (i + 1) % 100 == 0:
print(f"Iterasi {i+1}/{iterations}, Biaya: {cost:.4f}")
return weights, cost_history
Perhatikan bagaimana perhitungan gradien gradient = (1 / m) * (X.T @ (h - y))
secara ringkas mengimplementasikan rumus turunan menggunakan operasi matriks NumPy. Ini adalah contoh kekuatan vektorisasi. Setelah bobot optimal ditemukan, kita perlu fungsi untuk menggunakannya.
Langkah 5: Membuat Fungsi Prediksi
Setelah model dilatih (bobot ditemukan), kita perlu fungsi untuk membuat prediksi pada data baru (atau data latih).
def predict(X, weights, threshold=0.5):
"""Membuat prediksi kelas (0 atau 1).
Args:
X: Matriks fitur (termasuk kolom bias), shape (m, n+1).
weights: Vektor bobot terlatih, shape (n+1, 1).
threshold: Batas probabilitas untuk klasifikasi (default 0.5).
Returns:
predictions: Vektor prediksi kelas (0 atau 1), shape (m, 1).
"""
z = X @ weights
probabilities = sigmoid(z)
predictions = (probabilities >= threshold).astype(int) # Konversi boolean ke integer (0 atau 1)
return predictions
Untuk penggunaan yang lebih terstruktur, kita dapat membungkusnya dalam kelas.
Langkah 6 (Opsional): Membuat Kelas LogisticRegression
Untuk membuat kode lebih terstruktur dan reusable, kita bisa membungkus fungsi-fungsi tersebut dalam sebuah kelas. Ini adalah praktik yang baik untuk membuat model machine learning dari awal python secara lebih terorganisir.
class LogisticRegressionFromScratch:
def __init__(self, learning_rate=0.01, iterations=1000, add_bias=True, threshold=0.5):
self.learning_rate = learning_rate
self.iterations = iterations
self.add_bias = add_bias
self.threshold = threshold
self.weights = None
self.cost_history = []
def _add_bias(self, X):
m = X.shape[0]
return np.c_[np.ones((m, 1)), X]
def _sigmoid(self, z):
# Mencegah overflow
z = np.clip(z, -500, 500)
return 1 / (1 + np.exp(-z))
def _compute_cost(self, X, y, weights):
m = len(y)
z = X @ weights
h = self._sigmoid(z)
epsilon = 1e-5 # Mencegah log(0)
cost = - (1 / m) * np.sum(y * np.log(h + epsilon) + (1 - y) * np.log(1 - h + epsilon))
return cost
def fit(self, X, y):
"""Melatih model regresi logistik."""
if self.add_bias:
X_fit = self._add_bias(X)
else:
X_fit = X
m, n = X_fit.shape # m = sampel, n = fitur (+ bias jika ada)
# Inisialisasi bobot dengan nol atau nilai acak kecil
# np.random.seed(42) # opsional untuk reproduktifitas inisialisasi acak
# self.weights = np.random.randn(n, 1) * 0.01
self.weights = np.zeros((n, 1))
self.cost_history = []
for i in range(self.iterations):
z = X_fit @ self.weights
h = self._sigmoid(z)
gradient = (1 / m) * (X_fit.T @ (h - y))
self.weights = self.weights - self.learning_rate * gradient
# Hanya hitung & catat biaya jika bobot tidak NaN/Inf
if np.all(np.isfinite(self.weights)):
cost = self._compute_cost(X_fit, y, self.weights)
self.cost_history.append(cost)
else:
print(f"Iterasi {i+1}: Bobot menjadi tidak valid (NaN/Inf). Hentikan pelatihan.")
# Mungkin perlu mengurangi learning rate atau memeriksa data
break # Hentikan pelatihan jika bobot tidak valid
# Opsi cetak progress
# if (i + 1) % (self.iterations // 10 or 1) == 0:
# print(f"Iterasi {i+1}/{self.iterations}, Biaya: {cost:.4f}")
def predict_proba(self, X):
"""Memprediksi probabilitas kelas 1."""
if self.weights is None:
raise Exception("Model belum dilatih. Panggil fit() terlebih dahulu.")
if self.add_bias:
X_pred = self._add_bias(X)
else:
X_pred = X
# Periksa kompatibilitas shape
if X_pred.shape[1] != self.weights.shape[0]:
raise ValueError(f"Shape input X ({X_pred.shape}) tidak kompatibel dengan bobot model ({self.weights.shape})")
z = X_pred @ self.weights
return self._sigmoid(z)
def predict(self, X):
"""Memprediksi label kelas (0 atau 1)."""
probabilities = self.predict_proba(X)
return (probabilities >= self.threshold).astype(int)
Setelah mendefinisikan fungsi dan kelas, mari kita lihat cara menggunakannya.
Contoh Penggunaan dan Pelatihan Model
Sekarang mari kita gunakan fungsi atau kelas yang telah kita buat untuk melatih model pada data sintetis kita.
Menggunakan Fungsi Terpisah:
# Hyperparameters
learning_rate = 0.1
iterations = 1000
# Inisialisasi bobot (n+1, 1) - n adalah jumlah fitur asli (2) + 1 untuk bias
initial_weights = np.zeros((X_b.shape[1], 1))
print("\nMemulai Pelatihan Menggunakan Fungsi...")
# Latih model
final_weights, cost_history = gradient_descent(X_b, y, initial_weights, learning_rate, iterations)
print("\nPelatihan Selesai.")
print("Bobot akhir yang dipelajari (termasuk bias):")
print(final_weights)
# Buat prediksi pada data latih
predictions = predict(X_b, final_weights)
# Hitung akurasi sederhana
accuracy = np.mean(predictions == y) * 100
print(f"\nAkurasi pada data latih: {accuracy:.2f}%")
# Plot kurva biaya (opsional)
# plt.figure(figsize=(8, 6))
# plt.plot(range(len(cost_history)), cost_history) # Gunakan len(cost_history)
# plt.xlabel("Iterasi")
# plt.ylabel("Biaya (Log Loss)")
# plt.title("Kurva Pembelajaran Gradient Descent (Fungsi)")
# plt.show()
Menggunakan Kelas LogisticRegressionFromScratch
:
print("\nMemulai Pelatihan Menggunakan Kelas...")
# Inisialisasi dan latih model menggunakan kelas
# Gunakan X asli (tanpa bias manual), kelas akan menanganinya jika add_bias=True
model = LogisticRegressionFromScratch(learning_rate=0.1, iterations=1000, add_bias=True)
model.fit(X, y)
print("\nPelatihan Selesai.")
print("Bobot akhir yang dipelajari (dari kelas):")
print(model.weights) # Bobot tersimpan di dalam objek model
# Buat prediksi
# Gunakan X asli lagi
predictions_class = model.predict(X)
# Hitung akurasi
accuracy_class = np.mean(predictions_class == y) * 100
print(f"\nAkurasi pada data latih (menggunakan kelas): {accuracy_class:.2f}%")
# Akses history biaya
# print("\nHistory biaya (10 terakhir):", model.cost_history[-10:])
# Plot kurva biaya (opsional)
# plt.figure(figsize=(8, 6))
# plt.plot(range(len(model.cost_history)), model.cost_history) # Gunakan len(cost_history)
# plt.xlabel("Iterasi")
# plt.ylabel("Biaya (Log Loss)")
# plt.title("Kurva Pembelajaran Gradient Descent (Kelas)")
# plt.show()
Contoh ini menunjukkan cara melatih model regresi logistik dan menggunakannya untuk prediksi, sebuah bagian penting dari banyak numpy machine learning tutorial. Visualisasi dapat membantu pemahaman lebih lanjut.
Opsional Visualisasi Batas Keputusan dan Biaya
Jika Anda bekerja dengan data 2D seperti contoh kita, visualisasi batas keputusan (decision boundary) dapat memberikan intuisi yang baik tentang apa yang telah dipelajari model. Batas keputusan adalah garis (atau hyperplane dalam dimensi lebih tinggi) di mana output sigmoid adalah 0.5 (yaitu, z = X @ weights = 0
).
Kode untuk memplot batas keputusan biasanya melibatkan pembuatan grid titik-titik, memprediksi probabilitas pada grid tersebut, dan kemudian menggunakan fungsi seperti `plt.contour` untuk menggambar garis di mana probabilitasnya adalah 0.5.
# # --- Kode Visualisasi Batas Keputusan (Opsional) ---
# # Pastikan 'model' sudah dilatih atau 'final_weights' tersedia
# import matplotlib.pyplot as plt
# # Ambil bobot (w0=bias, w1=fitur1, w2=fitur2)
# w = model.weights # Pastikan model sudah .fit()
# # Plot data asli
# plt.figure(figsize=(10, 7))
# plt.scatter(X[y.flatten() == 0][:, 0], X[y.flatten() == 0][:, 1], color='blue', label='Kelas 0', alpha=0.6)
# plt.scatter(X[y.flatten() == 1][:, 0], X[y.flatten() == 1][:, 1], color='red', label='Kelas 1', alpha=0.6)
# # Buat grid untuk plot batas keputusan
# x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
# x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
# xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max, 200),
# np.linspace(x2_min, x2_max, 200))
# # Buat matriks fitur untuk grid (X untuk predict_proba kelas)
# grid = np.c_[xx1.ravel(), xx2.ravel()]
# # Hitung probabilitas pada grid menggunakan metode kelas
# try:
# Z = model.predict_proba(grid)
# Z = Z.reshape(xx1.shape)
# # Plot kontur batas keputusan (probabilitas = 0.5)
# plt.contour(xx1, xx2, Z, levels=[0.5], colors='green', linewidths=2)
# plt.contourf(xx1, xx2, Z, levels=[0, 0.5, 1], colors=['blue', 'red'], alpha=0.1) # Optional: fill regions
# except Exception as e:
# print(f"Gagal membuat plot batas keputusan: {e}")
# plt.xlabel("Fitur 1")
# plt.ylabel("Fitur 2")
# plt.title("Dataset Sintetis dengan Batas Keputusan Regresi Logistik")
# plt.legend()
# plt.grid(True, linestyle='--', alpha=0.5)
# plt.show()
# # Plot kurva biaya jika belum dan jika tersedia
# if hasattr(model, 'cost_history') and model.cost_history:
# plt.figure(figsize=(8, 6))
# plt.plot(range(len(model.cost_history)), model.cost_history)
# plt.xlabel("Iterasi")
# plt.ylabel("Biaya (Log Loss)")
# plt.title("Kurva Pembelajaran Gradient Descent")
# plt.grid(True, linestyle='--', alpha=0.5)
# plt.show()
# # --- Akhir Kode Visualisasi ---
Plot kurva biaya juga berguna untuk memastikan bahwa algoritma gradient descent berjalan dengan baik (biaya menurun seiring iterasi).
Kesimpulan dan Langkah Selanjutnya
Dalam tutorial ini, kita telah berhasil membangun model regresi logistik dari awal menggunakan Python dan NumPy (regresi logistik python from scratch). Kita telah membahas konsep inti: fungsi sigmoid untuk pemetaan probabilitas, fungsi biaya Binary Cross-Entropy untuk mengukur error, dan algoritma gradient descent untuk optimasi bobot. Anda sekarang memiliki pemahaman yang lebih baik tentang perhitungan matematis di balik algoritma ini, terutama bagaimana gradien dihitung dan bagaimana bobot diperbarui secara iteratif.
Memahami implementasi dasar seperti ini (regresi logistik python tanpa scikit learn) memberikan fondasi yang kuat sebelum beralih ke library machine learning yang lebih canggih. Sebagai langkah selanjutnya, Anda bisa mencoba menerapkan kode ini pada dataset yang berbeda, mengimplementasikan regularisasi (L1/L2) untuk mencegah overfitting, atau mulai menjelajahi library seperti Scikit-learn untuk melihat bagaimana konsep-konsep ini diabstraksi. Ini adalah langkah yang bagus bagi siapa saja yang mengikuti tutorial data science python pemula dan ingin mendalami algoritma inti.
Meskipun membangun model dari awal sangat edukatif, dalam aplikasi bisnis nyata, kecepatan pengembangan, skalabilitas, dan pemanfaatan model AI yang lebih canggih seringkali menjadi prioritas. Untuk kebutuhan tersebut, platform dan layanan seperti Kirim.ai dapat membantu mengakselerasi implementasi solusi AI. Kirim.ai menawarkan platform SaaS berbasis AI dengan berbagai alat siap pakai, pengembangan platform kustom (web & mobile), serta AI Agent untuk optimasi SEO berkelanjutan, memungkinkan bisnis memanfaatkan kekuatan AI secara efisien. Jika Anda tertarik untuk menerapkan solusi AI yang lebih kompleks atau membutuhkan pengembangan platform digital, Pelajari lebih lanjut tentang bagaimana Kirim.ai dapat membantu mendorong pertumbuhan bisnis Anda.
Tanggapan (0 )