DataLens

Regresi logistik dan klasifikasi

Ubah jumlah berbobot menjadi probabilitas dengan sigmoid, prediksi churn dengan regresi logistik, pilih ambang keputusan, dan baca koefisiennya.

Level: Menengah · 8 menit baca

Latihan modul ini

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

Idenya

Regresi linear memprediksi angka apa saja. Untuk pertanyaan ya/tidak ("apakah customer ini akan churn?") kita menginginkan probabilitas antara 0 dan 1. Regresi logistik mempertahankan jumlah berbobot dari regresi linear lalu memampatkannya ke rentang itu dengan fungsi sigmoid. Meskipun namanya regresi, ini model klasifikasi, dan model pertama yang dicoba untuk masalah ya/tidak apa pun.

Sigmoid

Model linear menghasilkan skor yang bisa berupa angka berapa pun, seperti -6 atau 4,2. Tapi "apakah pelanggan ini akan churn?" butuh jawaban antara 0 dan 1, sebuah probabilitas. Fungsi sigmoid memadatkan skor apa pun ke dalam rentang itu:

import numpy as np


def sigmoid(z):
    return 1 / (1 + np.exp(-z))


for z in [-6, -2, 0, 2, 6]:
    print(z, round(sigmoid(z), 3))
-6 0.002
-2 0.119
0 0.5
2 0.881
6 0.998

Modelnya: skor = w₁x₁ + w₂x₂ + ... + b, lalu probabilitas = sigmoid(skor). Training menemukan bobot yang meminimalkan log loss (dari modul model dan loss) pada data training.

Memprediksi churn

Sekarang tugas sebenarnya: latih model pada data churn lalu lihat seberapa baik model itu memprediksi pelanggan mana yang akan pergi. LogisticRegression dari scikit-learn menggabungkan skor linear dan sigmoid untukmu:

import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

churn = pd.read_csv("churn.csv")
FEATURES = ["tenure_months", "orders_90d", "days_since_last_order", "support_tickets", "app_user"]
X_train, X_test, y_train, y_test = train_test_split(
    churn[FEATURES], churn["churned"], test_size=0.25, random_state=42, stratify=churn["churned"])

model = LogisticRegression(max_iter=1000)
model.fit(X_train, y_train)

probabilities = model.predict_proba(X_test)[:, 1]
print(probabilities[:5].round(2))
print(model.predict(X_test)[:5])
print("accuracy:", round(model.score(X_test, y_test), 3))
[0.47 0.13 0.54 0.   0.59]
[0 0 1 0 1]
accuracy: 0.8

Ambang adalah keputusan bisnis

0,5 itu tidak sakral. Kalau voucher retensi itu murah dan kehilangan customer itu mahal, kamu lebih suka menghubungi lebih banyak orang: turunkan ambangnya.

for threshold in [0.3, 0.5, 0.7]:
    predicted = (probabilities >= threshold).astype(int)
    contacted = predicted.sum()
    caught = ((predicted == 1) & (y_test.to_numpy() == 1)).sum()
    print(f"threshold {threshold}: contact {contacted} customers, catch {caught} of {y_test.sum()} churners")
threshold 0.3: contact 48 customers, catch 30 of 33 churners
threshold 0.5: contact 35 customers, catch 24 of 33 churners
threshold 0.7: contact 22 customers, catch 18 of 33 churners

Ambang yang lebih rendah menangkap lebih banyak customer yang churn tapi juga menghubungi lebih banyak customer yang sebenarnya tidak akan pergi. Memilih ambang adalah pertukaran antara biaya customer churn yang terlewat dan biaya voucher yang terbuang, yang diubah modul metrik menjadi angka-angka yang presisi (precision dan recall).

Ide utama

Sebuah classifier memberi probabilitas; ambang mengubahnya menjadi keputusan. Pilih ambang dari biaya bisnis, bukan dari default.

Membaca koefisien

Seperti regresi linear, setiap koefisien menunjukkan arah dan kekuatan sebuah fitur, tapi dalam skala log-odds:

coefs = pd.Series(model.coef_[0], index=FEATURES).round(3)
print(coefs.sort_values())
orders_90d              -0.783
app_user                -0.736
tenure_months           -0.048
days_since_last_order    0.065
support_tickets          1.043
dtype: float64

Untuk fitur x dengan koefisien w, satu satuan tambahan mengalikan odds churn dengan exp(w):

print(round(np.exp(coefs["support_tickets"]), 2))
2.84

"Setiap tiket support tambahan mengalikan odds churn dengan kira-kira sebesar itu." Itulah temuan yang bisa dipakai tim customer service, dengan peringatan yang biasa: asosiasi, bukan bukti sebab-akibat.

Perhatikan bahwa besarnya koefisien tergantung satuan fitur (koefisien per hari terlihat kecil di samping koefisien per tiket). Untuk membandingkan tingkat kepentingan, skalakan fiturnya dulu (modul preprocessing).

Batas keputusan

Regresi logistik menggambar batas lurus di ruang fitur: churn di satu sisi, bertahan di sisi lain. Dengan satu fitur, itu adalah satu nilai batas:

one = LogisticRegression().fit(churn[["days_since_last_order"]], churn["churned"])
w, b = one.coef_[0][0], one.intercept_[0]
print("50% churn probability at", round(-b / w, 1), "days")
50% churn probability at 45.3 days

Pada sebanyak itu hari tanpa order, model menilai seorang customer 50/50. Saat batas yang sebenarnya melengkung atau bergantung pada kombinasi fitur, garis lurus tidak cukup, dan pohon atau ensemble (modul-modul selanjutnya) lebih baik.

Mengapa ini penting untuk kode yang bersih

Regresi logistik adalah baseline klasifikasi: cepat, stabil, bisa dijelaskan, dengan probabilitas yang mendukung keputusan. Pisahkan predict_proba dan ambangnya di kodemu (probabilities → decide(probabilities, threshold)), supaya aturan bisnisnya bisa berubah tanpa melatih ulang modelnya.

Rangkuman

Latihan modul ini

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

Berikutnya: Pembagian train/validation/test dan cross-validation →