Sıradaki

Lojistik regresyon eğit

Zorluk: Zor

sınıflandırmaoptimizasyon

Bu problemde lojistik regresyonu (logistic regression) sıfırdan eğitir ve yeni örnekler (sample) için tahmin (prediction) üretirsin. Lojistik regresyon, iki sınıflı sınıflandırma (classification) için doğrusal bir modeldir. Model üç adımda çalışır:

  1. Her özniteliği (feature) bir ağırlıkla (weight) çarpar ve toplar.
  2. Sapmayı (bias) ekler.
  3. Bu ham skoru (logit) sigmoid ile bir olasılığa çevirir.

Sigmoid, her sayıyı 0 ile 1 arasına sıkıştıran fonksiyondur.

train(X, y, X_test, lr, epochs) fonksiyonunu yaz. Girdiler şunlardır:

  • X: şekli (shape) (n, d) olan eğitim kümesi (training set). Her satır bir örnek, her sütun bir feature.
  • y: shape’i (n,) olan, 0 ya da 1 değerli etiketler (label).
  • X_test: sınıfı tahmin edilecek örneklerin (m, d) shape’indeki test kümesi (test set).
  • lr: öğrenme oranı (learning rate, η\eta).
  • epochs: epoch sayısı. Bir epoch, training set’in tamamı üzerinden bir turdur.

Model, tüm örnekler için p^=σ(Xw+b)\hat{\mathbf{p}} = \sigma(X\mathbf{w} + b) olasılıklarını hesaplar. w\mathbf{w} ve bb sıfırdan başlar. Eğitim, gradyan inişidir (gradient descent).

Her epoch’ta şunları yap:

  1. p^\hat{\mathbf{p}}‘yi hesapla.
  2. İkili çapraz entropi kaybının (binary cross-entropy loss) gradyanını (gradient) çıkar.
  3. w\mathbf{w} ve bb‘yi birden güncelle.

Güncellemede η\eta, lr değeridir:

w←w−η⋅1NX⊤(p^−y),b←b−η⋅1N∑i=1N(p^i−yi)\mathbf{w} \leftarrow \mathbf{w} - \eta \cdot \frac{1}{N} X^\top (\hat{\mathbf{p}} - \mathbf{y}), \qquad b \leftarrow b - \eta \cdot \frac{1}{N} \sum_{i=1}^{N} (\hat{p}_i - y_i)

Bu güncellemeyi tam epochs kez yap. Eğitimden sonra eşik (threshold) 0,50{,}5‘tir:

  • p^≥0,5\hat{p} \ge 0{,}5 olan test örneklerine sınıf 1 ver.
  • Diğerlerine sınıf 0 ver.

X_test’in her örneği için tahmin edilen sınıfı içeren, (m,) shape’inde bir tam sayı dizisi (array) döndür.

Örnekler

Girdi: X = [[1.0, 1.5], [2.0, 1.0], [1.5, 2.5], [2.5, 2.0], [4.0, 4.5], [5.0, 4.0], [4.5, 5.5], [5.5, 5.0]], y = [0, 0, 0, 0, 1, 1, 1, 1], X_test = [[0.5, 0.5], [2.0, 2.5], [3.5, 2.0], [2.0, 3.5], [3.2, 3.4], [4.0, 3.0], [6.0, 6.0]], lr = 0.5, epochs = 200
Çıktı: array([0, 0, 0, 0, 1, 1, 1])

Girdi: X = [[1.0], [2.0], [3.0], [7.0], [8.0], [9.0]], y = [0, 0, 0, 1, 1, 1], X_test = [[1.5], [2.0], [2.6], [3.0], [4.0], [5.0]], lr = 0.1, epochs = 50
Çıktı: array([0, 0, 1, 1, 1, 1])

Girdi: X = [[-3.0, 1.0], [-2.5, -1.0], [-4.0, 0.5], [-1.5, 2.0], [2.0, 0.5], [3.0, -1.0], [1.5, 1.5], [2.5, 2.0]], y = [1, 1, 1, 1, 0, 0, 0, 0], X_test = [[-5.0, 0.0], [-1.0, -2.0], [-0.5, 1.0], [0.5, -1.0], [1.0, 3.0], [4.0, 0.0]], lr = 0.5, epochs = 200
Çıktı: array([1, 1, 1, 0, 0, 0])

İpucu 1

Üç iş var:

  1. Bir sigmoid fonksiyonu yaz.
  2. epochs kez tekrarlanan bir eğitim döngüsü kur.
  3. Eğitimden sonra tahmin yap.

Sigmoid’i ayrı bir yardımcı fonksiyon olarak yaz. Kararlı sürümünü Kararlı sigmoid problemindeki gibi kurabilirsin.

İpucu 2

Her turda şunları yap:

  1. Olasılıkları hesapla: p = sigmoid(X @ w + b).
  2. Hatayı hesapla: p - y.
  3. w’nin gradyanını tek seferde hesapla: X.T @ (p - y) / n. Sonuç (d,) shape’indedir.
  4. Bias’ın gradyanını hesapla: np.mean(p - y). Bu, hatanın ortalamasıdır.
  5. İkisini de güncelle.

p’yi her turda yeniden hesapla. Çünkü w ve b değişti.

İpucu 3

Döngüden sonra aynı modeli X_test üzerinde kullan:

  1. sigmoid(X_test @ w + b) olasılıkları verir.
  2. >= 0.5 bir boolean array üretir.
  3. .astype(int) onu 0 ve 1’lere çevirir.

Gereksinimler

  • X (n, d), y (n,) ve X_test (m, d) shape’indedir. y tam sayılardan oluşur.
  • lr pozitif bir float’tur. epochs negatif olmayan bir tam sayıdır.
  • Dönüş değeri (m,) shape’inde, yalnızca 0 ve 1 içeren bir tam sayı array’i: bool ya da float array değil.
  • w ve b’yi sıfırdan başlat.
  • Her epoch’ta bütün X ile bir güncelleme yap. Tam epochs güncelleme olmalı. epochs = 0 ise hiç güncelleme yapma.
  • X’i olduğu gibi kullan: öznitelik ölçekleme (feature scaling) gibi bir ön işlem yok.
  • Eşikte sınıf 1: p^=0,5\hat{p} = 0{,}5 ise, yani logit z=0z = 0 ise, sınıf 1 ver. epochs = 0 iken her p^\hat{p} tam 0,50{,}5 olduğundan bütün tahminler 1 çıkar.
  • Feature’lar büyük değerler alabilir: birkaç yüz, hatta birkaç bin.
  • Logit’ler büyüdüğünde sigmoid nan ya da inf üretmemeli. Kararlı sigmoid problemindeki yazılış buna uygundur.
  • X’i, y’yi ve X_test’i değiştirme. Fonksiyon bittiğinde onu çağıran kodun array’leri aynı kalmalı.

Lojistik regresyonun logit’i z=Xw+bz = X\mathbf{w} + b, doğrusal regresyondaki tahminin aynısıdır. Fark, bunun sigmoid ile bir olasılığa çevrilmesidir.

p^≥0,5\hat{p} \ge 0{,}5 ile z≥0z \ge 0 aynı şeydir. Bu yüzden iki sınıfı ayıran karar sınırı (decision boundary) Xw+b=0X\mathbf{w} + b = 0 denklemiyle verilir:

  • İki feature’da bir doğru olur.
  • Üç feature’da bir düzlem olur.

Sigmoid ile binary cross-entropy bir araya gelince gradyan sadeleşir. Loss’un logit’e göre türevi p^i−yi\hat{p}_i - y_i olur.

Bu, doğrusal regresyonun MSE gradyanına çok benzer. Eğitim döngüsü de aynıdır. Yalnızca tahmini Xw+bX\mathbf{w} + b yerine σ(Xw+b)\sigma(X\mathbf{w} + b) ile alırsın.

Eğitim epochs adımda biter, yakınsama kontrolü yapmaz. Şu durumlar sonucu etkiler:

  • Learning rate küçükse ya da epoch azsa decision boundary henüz yerine oturmamıştır.
  • Sınıflar bir doğruyla tam ayrılabiliyorsa loss sıfıra yaklaşır ama weight’ler büyümeye devam eder. Model hiçbir zaman kesin bir çözüme varmaz.
  • Feature’lar çok farklı ölçeklerdeyse gradient descent yavaşlar. Bunu Doğrusal regresyon dersinde feature scaling olarak gördün.

Daha fazlası için Lojistik regresyon dersine bak.

Çözüm dört adımdır:

  1. Sigmoid’i kararlı yaz.
  2. w ve b’yi sıfırla.
  3. epochs kez olasılıkları hesapla ve ikisini birden güncelle.
  4. En sonda test set’i aynı modelle puanla.

Kodda bu adımlar şöyle görünür:

import numpy as npdef sigmoid(z: np.ndarray) -> np.ndarray:    e = np.exp(-np.abs(z))    return np.where(z >= 0, 1 / (1 + e), e / (1 + e))def train(X: np.ndarray, y: np.ndarray, X_test: np.ndarray, lr: float, epochs: int) -> np.ndarray:    n, d = X.shape    w = np.zeros(d)    b = 0.0    for _ in range(epochs):        p = sigmoid(X @ w + b)        w = w - lr * (X.T @ (p - y)) / n        b = b - lr * np.mean(p - y)    p_test = sigmoid(X_test @ w + b)    return (p_test >= 0.5).astype(int)

Sık yapılan hata, bias’ı (b) unutmaktır. Yalnızca w güncellenirse decision boundary orijinden geçmeye zorlanır. Sınıflar orijinden uzakta ayrılıyorsa model onları ayıramaz.

Bir diğer hata, p’yi döngünün dışında bir kez hesaplamaktır. O zaman gradyan hiç değişmez. Model aynı adımı epochs kez tekrarlar.

Bu problemde bir sorun mu var?

solution.py
import numpy as npdef train(X: np.ndarray, y: np.ndarray, X_test: np.ndarray, lr: float, epochs: int) -> np.ndarray:    """Lojistik regresyonu gradyan inişiyle eğitir; X_test'teki örneklerin sınıflarını (0 ya da 1) döndürür."""    # Kodunu buraya yaz.    return np.zeros(len(X_test), dtype=int)

İçindekiler

Geri bildirim

Tür