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
- Skor negatif yang besar menjadi probabilitas mendekati 0, skor positif yang besar mendekati 1, dan skor 0 menjadi tepat 0,5.
- Fungsinya mulus dan selalu di antara 0 dan 1, jadi output-nya bisa dibaca sebagai probabilitas.
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
predict_probamengembalikan dua kolom: P(kelas 0) dan P(kelas 1).[:, 1]mengambil probabilitas churn.predictmengubah probabilitas menjadi 0/1 memakai ambang (threshold) 0,5.scorememberi akurasi pada set test. Bandingkan dengan baseline 66,5% "tidak ada yang churn": modelnya jelas lebih baik.
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).
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
- Koefisien positif mendorong ke arah churn: lebih banyak hari sejak order terakhir, lebih banyak tiket support.
- Koefisien negatif menjauhkan: order yang lebih baru, tenure yang lebih lama, memakai aplikasi.
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
- Regresi logistik = jumlah berbobot → sigmoid → probabilitas; dilatih dengan meminimalkan log loss.
predict_proba(X)[:, 1]memberi P(kelas 1);predictmenerapkan ambang 0,5.- Pilih ambang dari biaya bisnis; ambang yang lebih rendah menangkap lebih banyak positif dan memunculkan lebih banyak alarm palsu.
- Koefisien: tanda = arah;
exp(w)= pengali odds per satuan. Skalakan fitur untuk membandingkannya. - Batas keputusannya lurus; masalah yang melengkung butuh model lain.