Sıradaki

En küçük kareler çözümü

Zorluk: Zor

doğrusal cebir

Bu problemde doğrusal regresyonda (linear regression) ortalama kare hatayı (mean squared error, MSE) en küçük yapan parametreleri bulursun. Gradyan inişine (gradient descent) gerek yoktur. Bir doğrusal denklem sistemi çözersin.

Bu yönteme en küçük kareler (least squares) denir. Çözüm bir formülle, yani kapalı formda (closed form) gelir.

least_squares(X, y) fonksiyonunu yaz. Girdiler şunlardır:

  • X: şekli (shape) (n, d) olan bir dizi (array). Her satır bir örnek (sample), her sütun bir öznitelik (feature) taşır.
  • y: hedef değerleri (target) içeren, (n,) shape’inde bir vektör.

(w, b) çiftini bir tuple olarak döndür. w, her feature için bir ağırlık (weight) içeren (d,) shape’inde bir array’dir. b, sapma (bias) olan bir float’tur.

Bias’ı weight’lerden ayrı tutmamak için şunu yap:

  1. XX‘in sağına bir birler sütunu ekle: X~=[X∣1]\tilde{X} = [X \mid \mathbf{1}]. Matris (n, d + 1) shape’indedir.
  2. Parametreleri tek bir vektörde topla: θ=(w,b)\boldsymbol{\theta} = (\mathbf{w}, b).

MSE’nin gradyanı sıfır olduğunda kayıp (loss) en küçüktür. Gradyanı sıfıra eşitlemek normal denklemleri (normal equations) verir:

X~⊤X~ θ=X~⊤y\tilde{X}^\top \tilde{X}\,\boldsymbol{\theta} = \tilde{X}^\top \mathbf{y}

Kapalı form şöyle yazılır:

θ=(X~⊤X~)−1X~⊤y\boldsymbol{\theta} = (\tilde{X}^\top \tilde{X})^{-1}\tilde{X}^\top \mathbf{y}

Ama tersini alma: sistemi np.linalg.solve ile çöz. Sonra θ\boldsymbol{\theta}‘dan w\mathbf{w}‘yi ve bb‘yi ayır.

Örnekler

Girdi: X = [[1.0, 2.0], [2.0, 1.0], [3.0, 5.0], [4.0, 3.0], [5.0, 6.0]], y = [3.0, 4.0, 9.0, 8.0, 12.0]
Çıktı: (array([1.28333333, 0.91666667]), 0.23333333333332915)

Girdi: X = [[1.0], [3.0]], y = [2.0, 8.0]
Çıktı: (array([3.]), -1.000000000000001)

Girdi: X = [[-2.0, 1.0, 0.5], [-1.0, -1.0, 2.0], [0.0, 3.0, -1.0], [1.0, 0.0, 1.5], [2.0, -2.0, -0.5], [3.0, 1.0, 1.0]], y = [-4.0, 1.0, 3.5, 2.0, -1.0, 5.0]
Çıktı: (array([1.35470735, 1.19913395, 1.08798142]), -0.6283874853029737)

İpucu 1

Birler sütununu iki adımda ekle:

  1. np.ones((n, 1)) ile bir birler sütunu oluştur.
  2. np.hstack ile onu X’in sağına ekle.

Bu genişletilmiş matrise Xb diyelim. Weight’ler ve bias tek bir vektörde toplanır. Bu yüzden Xb (n, d + 1) shape’inde olmalı.

İpucu 2

Denklemin iki yanı şöyledir:

  • Sol taraf Xb.T @ Xb, (d + 1, d + 1) shape’inde bir matristir.
  • Sağ taraf Xb.T @ y, (d + 1,) shape’inde bir vektördür.

np.linalg.solve(A, c), Ax=cA\mathbf{x} = \mathbf{c} sistemini x\mathbf{x} için çözer.

İpucu 3

Çözüm vektörünü şöyle parçala:

  • Son eleman birler sütununa karşılık gelir, yani bias’tır: theta[-1].
  • Ondan öncekiler weight’lerdir: theta[:-1].

Gereksinimler

  • X (n, d) ve y (n,) shape’inde array’lerdir.
  • X~\tilde{X} matrisinin sütunları doğrusal bağımsızdır, yani X~⊤X~\tilde{X}^\top \tilde{X} tersinirdir. Aksi durumları ele alman gerekmez.
  • (w, b) biçiminde bir tuple döndür. w (d,) shape’inde bir NumPy array’i olmalı, (d, 1) bir sütun değil.
  • b tek bir float’tur. theta[-1]’in verdiği np.float64 de kabul edilir.
  • Normal denklemleri np.linalg.solve ile çöz. np.linalg.inv, np.linalg.pinv ya da hazır çözücü np.linalg.lstsq kullanma.
  • Birler sütununu sen ekle.
  • X’i ve y’yi değiştirme. Fonksiyon bittiğinde onu çağıran kodun array’leri aynı kalmalı.
  • Örnekler üzerinde döngü kullanma: birkaç matris işlemi yeter.

MSE, θ\boldsymbol{\theta}‘nın karesel bir fonksiyonudur: yüzeyi bir çanaktır ve en alçak noktasında gradyan sıfırdır. Gradyan şudur:

2NX~⊤(X~θ−y)\frac{2}{N}\tilde{X}^\top(\tilde{X}\boldsymbol{\theta} - \mathbf{y})

Onu sıfıra eşitlemek normal denklemleri verir.

Küçük bir örnek: x=(1,2,3,4)x = (1, 2, 3, 4) ve y=(2,3,5,6)y = (2, 3, 5, 6) olsun. Matrisler şöyle çıkar:

X~⊤X~=[3010104],X~⊤y=(47,16)\tilde{X}^\top\tilde{X} = \begin{bmatrix}30 & 10 \\ 10 & 4\end{bmatrix}, \qquad \tilde{X}^\top\mathbf{y} = (47, 16)

Yani 30w+10b=4730w + 10b = 47 ve 10w+4b=1610w + 4b = 16. Sistemi şöyle çöz:

  1. İkinci denklemden b=4−2,5wb = 4 - 2{,}5w çıkar.
  2. Bunu birinci denkleme yerleştir: 5w=75w = 7.
  3. Böylece w=1,4w = 1{,}4 ve b=0,5b = 0{,}5 bulunur.

Ters matrisi hesaplayıp çarpmak ile sistemi doğrudan çözmek matematikte aynı sonucu verir. Ama solve genellikle daha hızlı ve sayısal olarak daha kararlıdır. Üstelik çözüm için tersin bütün elemanlarına ihtiyaç yoktur.

Normal denklemlerin de bir sınırı vardır. X~⊤X~\tilde{X}^\top\tilde{X} matrisinin koşul sayısı (condition number), X~\tilde{X} matrisinin koşul sayısının karesidir. Bu yüzden sütunlar neredeyse doğrusal bağımlıysa sonuç güvenilmez olur.

Kütüphaneler bu yüzden çoğu zaman X~\tilde{X} matrisini doğrudan QR ya da SVD ile çözen np.linalg.lstsq fonksiyonunu kullanır.

Sütunlar tam doğrusal bağımlıysa X~⊤X~\tilde{X}^\top\tilde{X} tekildir ve solve hata verir.

Kapalı form tek adımda tam sonucu verir. Ama iki sınırı vardır:

  • dd büyüdükçe d×dd \times d bir sistemi çözmek pahalıdır.
  • Sinir ağları gibi modellerin kapalı formu yoktur.

Gradient descent bu yüzden her modelde işe yarayan genel yöntemdir. Doğrusal regresyonda ikisi aynı noktaya varır.

Daha fazlası için Ölçekleme, mini-batch ve kapalı form dersine bak.

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

  1. Birler sütununu ekle.
  2. Normal denklemin iki yanını kur.
  3. np.linalg.solve ile çöz.
  4. Çözümü ayır: son eleman bias, öncekiler weight’lerdir.

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

import numpy as npdef least_squares(X: np.ndarray, y: np.ndarray) -> tuple[np.ndarray, float]:    Xb = np.hstack([X, np.ones((X.shape[0], 1))])  # bias için birler sütunu    theta = np.linalg.solve(Xb.T @ Xb, Xb.T @ y)    return theta[:-1], float(theta[-1])

Sık yapılan hata, formülü olduğu gibi yazmaktır: np.linalg.inv(Xb.T @ Xb) @ Xb.T @ y.

Sonuç genellikle aynı çıkar, ama kod gereksiz yere ters matris hesaplar ve sayısal olarak daha zayıftır. Bu problemde inv kabul edilmez.

Bir diğer hata, birler sütununu unutmaktır. Model orijinden geçmeye zorlanır ve b hiç öğrenilmez.

Bu problemde bir sorun mu var?

solution.py
import numpy as npdef least_squares(X: np.ndarray, y: np.ndarray) -> tuple[np.ndarray, float]:    """Normal denklemleri çözerek en küçük kareler çözümünü (w, b) olarak döndürür."""    # Kodunu buraya yaz.    return np.zeros(X.shape[1]), 0.0

İçindekiler

Geri bildirim

Tür