ML Temelleri · Modül 2 · 22 dakika

Ölçekleme, mini-batch ve kapalı form

Ham öznitelikler (feature)öznitelik (feature): Bir örneği tanımlayan ve modele girdi olarak verilen ölçülebilir değerlerden biri. eğitimi dar bir vadiye hapseder. Öznitelik ölçekleme (feature scaling) bu vadiyi yuvarlar. Bu derste ölçeklemeyi uygulayacak, gradyanı mini yığınlardan (mini-batch)mini yığın (mini-batch): Bir parametre güncellemesinin gradyanını hesaplamak için kullanılan, veri kümesinden alınmış küçük örnek grubu. hesaplamayı öğrenecek ve aynı çizgiyi kapalı formda (closed form) tek hamlede bulacaksın.

Bu ders, Doğrusal regresyon dersinin devamıdır. Oradaki gradyanı, eğitim döngüsünü ve dört evin verisini ilk blokta yeniden kurarsın.

Bu derste şunları yapacaksın:

  • Vadiyi yuvarlayınca öğrenme oranının (learning rate)öğrenme oranı (learning rate): Gradyan inişinde atılan adımın büyüklüğü. Çok küçükse eğitim yavaşlar, çok büyükse kayıp büyür. 1000 kat büyüyebildiğini ve yüz binlerce epochepoch: Eğitimde veri kümesinin tamamı üzerinden bir tur; her örnek bir kez kullanılmış olur.’un yirmiye indiğini göreceksin.
  • Stokastik gradyan inişini (stochastic gradient descent, SGD)stokastik gradyan inişi (stochastic gradient descent, SGD): Gradyanı her adımda bütün veriden değil, rastgele seçilmiş küçük bir örnek grubundan (mini yığın) hesaplayan gradyan inişi. kuracaksın.
  • Örnek grubunun büyüklüğünü değiştirip kaybın nasıl titrediğini göreceksin.
  • En iyi çizgiyi gradyan inişi (gradient descent)gradyan inişi (gradient descent): Kaybı azaltmak için parametreleri gradyanın tersine küçük adımlarla güncelleyen yöntem. olmadan, tek bir doğrusal sistemi çözerek bulacaksın.

Öznitelik ölçekleme

Öznitelikleri ölçeklemek vadiyi yuvarlar; aynı çizgiye çok daha çabuk varırsın.

Önceki dersin kodu

Model, ağırlıklar (weight)ağırlık (weight): Bir girdiyle çarpılarak onun çıktıya ne kadar katkıda bulunacağını belirleyen parametre. Doğrusal modelde her özniteliğin kendi ağırlığı vardır. w\mathbf{w} ve sapma (bias)sapma (bias): Girdilerle çarpılmadan çıktıya eklenen parametre. Doğrusal modelde bütün öznitelikler 0 iken modelin verdiği tahmindir. bb ile tahmin yapar. Kayıp (loss) ortalama kare hatadır (mean squared error, MSE)ortalama kare hata (mean squared error, MSE): Tahminlerle hedef değerler arasındaki farkların karelerinin ortalaması. Büyük hataları küçüklerden daha ağır cezalandırır.. Veri, dört evlik aynı veri kümesidir (dataset)veri kümesi (dataset): Her biri öznitelikler ve bir hedef değerden oluşan örneklerin tamamı..

Aşağıdaki blok önceki dersin fonksiyonlarını ve verisini yeniden kurar. Bu sayfadaki bütün bloklar ona dayanır.

import numpy as npdef mse_loss(X, y, w, b):    return np.mean((X @ w + b - y) ** 2)def mse_gradient(X, y, w, b):    n = X.shape[0]    error = X @ w + b - y              # y_hat - y    grad_w = 2 / n * (X.T @ error)    grad_b = 2 / n * np.sum(error)    return grad_w, float(grad_b)def train(X, y, lr=0.1, epochs=100):    w = np.zeros(X.shape[1])    b = 0.0    for _ in range(epochs):        grad_w, grad_b = mse_gradient(X, y, w, b)        w = w - lr * grad_w        b = b - lr * grad_b    return w, bX = np.array([[40.], [60.], [80.], [100.]])    # metrekarey = np.array([10., 15., 17., 22.])             # kira, bin TLw, b = train(X, y, lr=0.0001, epochs=1)print(w, b)                                    # → [0.243] 0.0032

Tek epoch’un çıktısı, önceki derste elle bulduğumuz 0,243 ve 0,0032’dir.

Standartlaştırma

Vadiyi yuvarlamanın yolu feature scaling’dir: öznitelikleri benzer bir aralığa getirirsin. En yaygın biçimi standartlaştırmadır (standardization): her öznitelikten ortalamasını çıkar, standart sapmasına (standard deviation) böl.

zi=xi−μσz_i = \frac{x_i - \mu}{\sigma}

μ\mu ortalamadır. σ\sigma standart sapmadır: ortalamadan farkların karelerini ortala, sonra karekökünü al. Dört evde:

μ=70,σ=900+100+100+9004=500≈22,36\mu = 70, \qquad \sigma = \sqrt{\frac{900 + 100 + 100 + 900}{4}} = \sqrt{500} \approx 22{,}36

Ölçekli değerler −1,34; −0,45; 0,45; 1,34-1{,}34;\ -0{,}45;\ 0{,}45;\ 1{,}34 olur. Ortalamayı çıkarmak wbwb terimini, yani vadinin eğikliğini yok eder; standart sapmaya bölmek w2w^2‘nin katsayısını 1’e indirir.

import numpy as npx = np.array([40., 60., 80., 100.])mu, sigma = x.mean(), x.std()       # ortalama ve standart sapmaprint(mu, round(sigma, 2))          # → 70.0 22.36z = (x - mu) / sigmaprint(np.round(z, 2))               # → [-1.34 -0.45  0.45  1.34]print(round(z.mean(), 6), round(np.mean(z ** 2), 6))   # → 0.0 1.0

Ölçekli özniteliğin ortalaması 0, karelerinin ortalaması 1’dir. Bir sonraki bölüm bu iki sayıyı kullanır. Öznitelikler birbiriyle ilişkiliyse ölçekleme çanağı tam yuvarlamaz, ama yine çok yardımcı olur.

Ölçekli çanak

Ölçekli özniteliğin MSE’sinde iki şey değişir:

  • Ortalaması 0 olduğundan wbwb terimi kaybolur.
  • Karelerinin ortalaması 1 olduğundan w2w^2‘nin katsayısı b2b^2‘ninkine eşitlenir.

Parametrelere wzw_z ve bzb_z dersek, gradyanlar 2 (wz−ort(zy))2\,(w_z - \text{ort}(zy)) ve 2 (bz−ort(y))2\,(b_z - \text{ort}(y)) olur. Dip, ikisinin de sıfır olduğu noktadır: wz=4,25w_z = 4{,}25 ve bz=16b_z = 16. Loss şuna yaklaşır:

L(wz,bz)≈(wz−4,25)2+(bz−16)2+0,45L(w_z, b_z) \approx (w_z - 4{,}25)^2 + (b_z - 16)^2 + 0{,}45

Eş yükselti eğrileri (contour lines) merkezi (4,25; 16)(4{,}25;\ 16) olan çemberlerdir; çanak yuvarlaktır. Bir adım, wzw_z‘nin ve bzb_z‘nin dipten uzaklığını 1−2η1 - 2\eta ile çarpar. Bu, Gradyan inişi dersindeki L(w)=w2L(w) = w^2 ile aynı kuraldır; orada η=0,5\eta = 0{,}5 tek adımda minimuma götürüyordu, burada da götürür.

Dibi ham birimlere çevirirsen ilk dersteki en iyi çizgiyi bulursun:

w=4,25/22,36=0,19,b=16−0,19⋅70=2,7w = 4{,}25 / 22{,}36 = 0{,}19, \qquad b = 16 - 0{,}19 \cdot 70 = 2{,}7
import numpy as npx = np.array([40., 60., 80., 100.])y = np.array([10., 15., 17., 22.])mu, sigma = x.mean(), x.std()Z = ((x - mu) / sigma).reshape(-1, 1)        # ölçekli öznitelik, (4, 1)# train ve mse_loss: önceki bloklardanw_z, b_z = train(Z, y, lr=0.5, epochs=1)     # eta = 0.5: tek adımda dibeprint(np.round(w_z, 2), b_z)                 # → [4.25] 16.0print(round(mse_loss(Z, y, w_z, b_z), 2))    # → 0.45w = w_z[0] / sigma                           # ham birime çevirb = b_z - w * muprint(round(w, 2), round(b, 2))              # → 0.19 2.7

İki eğitimin karşılaştırması

İki eğitimi aynı noktadan, w=0w = 0 ve b=0b = 0‘dan başlatıp öğrenme eğrilerini (learning curve) yan yana koyalım. Ölçeksizde η=0,0001\eta = 0{,}0001, ölçeklide η=0,1\eta = 0{,}1 kullanıyoruz; ikisi de kendi çanağında yakınsayan bir learning rate’tir.

EpochLoss, ölçeksizLoss, ölçekli
0274,5274,5
12,88175,84
31,1272,29
101,123,61
201,120,49
1001,120,45
10001,100,45
10 bin0,920,45
100 bin0,470,45

Tabloya bak:

  • Ölçeksiz eğitim ilk epoch’larda hızlı görünür, çünkü dar yönü hemen çözer. Sonra yüz binlerce epoch boyunca uzun yönde sürünür.
  • Loss’un 0,5’in altına inmesi ölçekli eğitimde 20 epoch, ölçeksizde yaklaşık 70 bin epoch alır. Üstelik ölçekli eğitimin learning rate’i 1000 kat büyüktür.

Aynı sayıları kodla üret:

import numpy as np# train ve mse_loss: önceki bloklardanx = np.array([40., 60., 80., 100.])y = np.array([10., 15., 17., 22.])X = x.reshape(-1, 1)Z = ((x - x.mean()) / x.std()).reshape(-1, 1)epochs = (1, 3, 10, 20, 100, 1000)raw = [mse_loss(X, y, *train(X, y, lr=0.0001, epochs=k)) for k in epochs]scaled = [mse_loss(Z, y, *train(Z, y, lr=0.1, epochs=k)) for k in epochs]print(*[f"{v:.2f}" for v in raw])      # → 2.88 1.12 1.12 1.12 1.12 1.10print(*[f"{v:.2f}" for v in scaled])   # → 175.84 72.29 3.61 0.49 0.45 0.45w, b = train(X, y, lr=0.0001, epochs=100_000)print(f"{mse_loss(X, y, w, b):.2f}")    # → 0.47

Ölçekleme çözümü değiştirmez; aynı çizgiye ne kadar çabuk varacağını değiştirir. Aynı farkı Doğrusal regresyon dersindeki grafikte gör. “Ölçekli feature” seçeneğini seç, yolu izle, sonra “Ölçeksiz” seçeneğine dön:

  • Eş yükselti eğrileri, uzun ve eğik elipslerden yuvarlak çemberlere döner.
  • Aynı η=0,55\eta = 0{,}55 ile ölçeksiz çanakta top vadi tabanı boyunca sürünür; ölçekli çanakta yaklaşık 2 adımda dibe iner.
  • İç grafikte çizgi, ölçekli çanakta çok daha çabuk veriye oturur.

Ölçekli modelle tahmin

Dikkat

Ölçekli feature’la eğitilen modelin wzw_z ve bzb_z değerleri ham feature’ın birimini bilmez. Yeni bir ev için şunu yap:

  1. Eğitimdeki μ\mu ve σ\sigma ile ölçekle: 90 metrekare için z=(90−70)/22,36≈0,89z = (90 - 70)/22{,}36 \approx 0{,}89.
  2. Tahmin et: 4,25⋅0,89+16≈19,84{,}25 \cdot 0{,}89 + 16 \approx 19{,}8 bin TL.

Ham birimlerdeki 0,19⋅90+2,7=19,80{,}19 \cdot 90 + 2{,}7 = 19{,}8 ile aynıdır. Yeni verinin kendi ortalamasını kullanma.

import numpy as npx = np.array([40., 60., 80., 100.])mu, sigma = x.mean(), x.std()           # eğitimdeki ortalama ve standart sapmaz_new = (90 - mu) / sigma               # yeni ev: 90 metrekareprint(round(z_new, 2))                  # → 0.89print(round(4.25 * z_new + 16, 1))      # → 19.8print(round(0.19 * 90 + 2.7, 1))        # → 19.8
Kısa soruÖlçeksiz feature'larla doğrusal regresyon eğitiyorsun ve loss 500 epoch boyunca neredeyse değişmiyor. Bundan ne çıkarırsın?

Cevabı göster

Model vadide yavaş ilerliyor olabilir; feature'ları ölçekleyip yeniden dene. Ölçeksiz feature çanağı uzun ve eğik yapar. Learning rate küçük kalmak zorundadır ve vadinin boyunca ilerleme yavaşlar. Ölçekleme bunu düzeltir.

Veriyi parçalara bölmek

Gradyan bir ortalamadır ve bir ortalamaya yaklaşmak için küçük bir örnek grubu çoğu zaman yeter. Tam gradyan her güncellemede bütün NN örneği ister. Bir milyon örnekte bu, tek adım için bir milyon tahmin ve hata demektir.

Örnek grupları

Gradyanı hesaplarken kullandığın örnek grubuna yığın (batch) denir. Bütün veri kümesini kullanmak tek bir büyük batch’tir; verinin küçük bir parçasını kullanmak mini-batch’tir.

BB örnekli bir mini-batch’in MSE’sine LBL_B diyelim. Gradyan aynı formülle çıkar; yalnızca NN yerine BB, XX yerine mini-batch’in satırları XBX_B girer:

∂LB∂w=2B XB⊤(y^B−yB)\frac{\partial L_B}{\partial \mathbf{w}} = \frac{2}{B}\,X_B^\top(\hat{\mathbf{y}}_B - \mathbf{y}_B)

Dört evi iki mini-batch’e bölelim; önce 40 ve 60 metrekarelik evler, sonra 80 ve 100 metrekarelik evler. Model yine A (w=0,2w = 0{,}2, b=2b = 2). İlk mini-batch’te hatalar 0 ve −1 olur:

∂LB∂w=22 (0⋅40−1⋅60)=−60\frac{\partial L_B}{\partial w} = \frac{2}{2}\,(0 \cdot 40 - 1 \cdot 60) = -60

Mini-batch gradyanı

İkinci mini-batch aynı yolla çıkar:

Mini-batchEvler (m²)HatalarGradyan (w; b)(w;\ b)
140, 600, −1(−60; −1)(-60;\ -1)
280, 1001, 0(80; 1)(80;\ 1)
Ortalama(10; 0)(10;\ 0)

İki mini-batch’in gradyanı zıt yönleri bile gösterir, ama ortalamaları bütün veriyle hesapladığımız gradyandır.

Her mini-batch gradyanı, gerçek gradyanın gürültülü bir yaklaşık değeridir. Rastgele seçilmiş bir mini-batch’in gradyanı ortalamada tam gradyana eşittir, tek tek ise değil.

import numpy as np# mse_gradient: bu dersin ilk bloğundaki fonksiyonX = np.array([[40.], [60.], [80.], [100.]])y = np.array([10., 15., 17., 22.])w, b = np.array([0.2]), 2.0g1 = mse_gradient(X[:2], y[:2], w, b)     # 40 ve 60 metrekareg2 = mse_gradient(X[2:], y[2:], w, b)     # 80 ve 100 metrekareprint(g1[0], g1[1])                       # → [-60.] -1.0print(g2[0], g2[1])                       # → [80.] 1.0print((g1[0] + g2[0]) / 2)                # → [10.]

Stokastik gradyan inişi

Gradyanı her adımda bir mini-batch’ten hesaplayan gradient descent’e SGD denir. “Stokastik” rastgelelik içeren demektir; her adımın gradyanı, hangi örneklerin seçildiğine bağlıdır. Kütüphaneler SGD derken çoğunlukla bu yöntemi kasteder. Dar anlamda SGD, B=1B = 1 olan uç durumdur.

import numpy as np# mse_gradient: bu dersin ilk bloğundaki fonksiyonX = np.array([[40.], [60.], [80.], [100.]])y = np.array([10., 15., 17., 22.])w, b, lr = np.array([0.2]), 2.0, 0.0001grad_full, _ = mse_gradient(X, y, w, b)            # bütün verigrad_batch, _ = mse_gradient(X[:2], y[:2], w, b)   # birinci mini-batchprint(w - lr * grad_full)     # → [0.199]print(w - lr * grad_batch)    # → [0.206]

Tam gradyan ww‘yi küçültür, birinci mini-batch büyütür. Gürültü budur.

Epoch başına güncelleme

Epoch’un tanımı değişmez: dataset’in her örneği bir kez kullanılınca bir epoch biter. Ama bir epoch artık birden çok güncelleme içerir:

  • NN, BB‘ye bölünüyorsa: 1000 örnek ve B=100B = 100 ile bir epoch 10 güncellemedir.
  • NN, BB‘ye bölünmüyorsa: son mini-batch daha küçük kalır. N=10N = 10 ve B=4B = 4 için mini-batch’ler 4, 4 ve 2 örnektir; bir epoch 3 güncellemedir.
import mathprint(math.ceil(1000 / 100))                        # → 10print(math.ceil(10 / 4))                            # → 3print([min(4, 10 - s) for s in range(0, 10, 4)])    # → [4, 4, 2]

Karıştırma ve mini-batch üretimi

Veri sıralıysa, örneğin evler küçükten büyüğe diziliyse, ardışık mini-batch’ler verinin yalnızca bir kesimini temsil eder. Yukarıdaki iki mini-batch’in gradyanlarının ne kadar ayrıştığını gördün. Bu yüzden her epoch şu adımlarla çalışır:

  1. Örnekleri karıştır.
  2. Karışmış sırayı BB‘lik parçalara böl. Her epoch’ta farklı gruplar çıkar.
  3. Her mini-batch için gradyanı hesapla ve parametreleri bir kez güncelle.

Karıştırmayı sabit bir tohumla (seed) yaparsan eğitim her çalıştırmada aynı sonucu verir.

İlk iki adım bir fonksiyon olur:

import numpy as npdef minibatches(X, y, batch_size, seed=0):    n = X.shape[0]    rng = np.random.default_rng(seed)    order = rng.permutation(n)                  # 1. karıştır    batches = []    for start in range(0, n, batch_size):       # 2. parçalara böl        idx = order[start : start + batch_size]        batches.append((X[idx], y[idx]))    return tuple(batches)X = np.arange(10.0).reshape(10, 1)              # 10 örnek, 1 featurey = np.arange(10.0)batches = minibatches(X, y, 4)sizes = [len(y_b) for _, y_b in batches]print(sizes)                                    # → [4, 4, 2]used = np.concatenate([y_b for _, y_b in batches])print(np.sort(used))                            # → [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]again = minibatches(X, y, 4)                    # aynı tohumsame = all(np.array_equal(p[0], q[0]) for p, q in zip(batches, again))print(same)                                     # → True

Üçüncü adım, her mini-batch için bir güncellemedir. Aşağıdaki figürle aynı sayıları görmek için karıştırmayı sabitleyelim: orders, 20 epoch’un karışmış sıralarıdır (seed 259) ve her batch boyutu aynı sıraları kullanır. Her rakam bir evin numarasıdır (0’dan 3’e); örneğin 3102, ilk epoch’ta evlerin 3, 1, 0 ve 2 sırasıyla gelmesidir.

# 20 epoch'un karıştırılmış sırası (seed 259); her rakam bir evin numarasıorders = [[int(c) for c in s] for s in          "3102 2031 3012 2310 1032 2310 1032 2013 1230 2301 "          "0321 1023 2130 3102 2103 0231 0213 2013 1032 0312".split()]print(len(orders), orders[0])           # → 20 [3, 1, 0, 2]

Güncelleme döngüsü orders’ın her sırasını BB‘lik parçalara böler:

def sgd(X, y, batch_size, lr, orders):    w = np.zeros(X.shape[1])    b = 0.0    curve = [mse_loss(X, y, w, b)]    for order in orders:                        # epoch başına bir sıra        for start in range(0, len(y), batch_size):            idx = order[start : start + batch_size]            grad_w, grad_b = mse_gradient(X[idx], y[idx], w, b)            w = w - lr * grad_w            b = b - lr * grad_b        curve.append(mse_loss(X, y, w, b))      # epoch sonunda loss    return w, b, curvex = np.array([40., 60., 80., 100.])y = np.array([10., 15., 17., 22.])Z = ((x - x.mean()) / x.std()).reshape(-1, 1)   # ölçekli featurew_z, b_z, curve = sgd(Z, y, batch_size=2, lr=0.25, orders=orders)print(np.round(w_z, 2), round(b_z, 2))          # → [4.22] 15.98print(round(curve[-1], 3))                      # → 0.451

Her epoch’ta iki güncelleme olduğu için 20 epoch, 40 güncelleme eder. Parametreler w=b=0w = b = 0‘dan başlar ve çanağın dibi olan (4,25; 16)(4{,}25;\ 16) noktasının çevresinde kalır.

Batch boyutu

BB bir denge ayarıdır. Yöntemler BB‘ye göre ayrılır:

YöntemBBGradyanAdımEpoch başına güncelleme
Tam batch (batch gradient descent)NNTamPahalı1
Mini-batch1<B<N1 < B < NGürültülüDaha ucuz⌈N/B⌉\lceil N / B \rceil
SGD, dar anlamda (tek örnek)1Çok gürültülüEn ucuzNN

Gürültü yüzünden sabit bir learning rate’le SGD minimumun tam üstüne oturmaz, çevresinde dolanır. Learning curve de düzgün inmez, hafifçe titrer.

Üç batch boyutunu aynı koşulda karşılaştıralım:

  • ölçekli feature, w=b=0w = b = 0‘dan başlangıç;
  • η=0,25\eta = 0{,}25 ve 20 epoch;
  • yukarıdaki sabit karıştırma (orders).

Her satır şunları yazar: BB, epoch başına güncelleme sayısı, 1. epoch sonunda loss ve 8. epoch’tan sonraki loss’un en küçük ile en büyük değeri.

import math# sgd, orders ve Z: önceki bloklardanfor bs in (4, 2, 1):    _, _, curve = sgd(Z, y, batch_size=bs, lr=0.25, orders=orders)    low, high = min(curve[8:]), max(curve[8:])     # 8. epoch'tan sonra    print(bs, math.ceil(4 / bs), round(curve[1], 2), f"{low:.2f}-{high:.2f}")# → 4 1 68.96 0.45-0.45# → 2 2 12.28 0.45-0.49# → 1 4 4.59 0.51-1.03

Aralığa bak:

  • B=4B = 4, yani tam batch: loss 0,45’te sabit kalır, gürültü yoktur.
  • B=2B = 2: loss 0,45 ile 0,49 arasında dolanır.
  • B=1B = 1: loss 0,51 ile 1,03 arasında titrer.

Batch küçüldükçe güncelleme sayısı artar, ama her güncelleme daha gürültülüdür. Küçük batch’te ilk epoch’lar çok hızlı iner: 1. epoch sonunda loss B=4B = 4 için 68,96, B=1B = 1 için 4,59’dur.

Aşağıdaki figürde batch boyutunu 1, 2 ve 4 arasında sürükle ve loss eğrisine bak. Figür, yukarıdaki kodla aynı koşulu kullanır: η=0,25\eta = 0{,}25, 20 epoch, ölçekli feature, w=b=0w = b = 0 ve aynı karışık sıralar.

batch_size_curve

Kayıp · loss (MSE, log ölçek) · ölçekli feature · η = 0,25 · w = b = 0'dan · tohum 259

11010005101520epochen küçük kayıp 0,45274,5B = 1 · tek örnekB = 4 · tam batch
Epoch başına güncelleme4
1. epoch sonunda kayıp4,59
20. epoch sonunda kayıp0,51

B = 1 · epoch başına 4 güncelleme, 20 epoch'ta 80 güncelleme 8.–20. epoch arası kayıp 0,51 ile 1,03 arasında · tam batch'te 0,45 ile 0,45

Dört evlik bir veride mini-batch’e gerek yoktur. Asıl yeri büyük veri kümeleri ve sinir ağlarıdır (neural network); onlar neredeyse her zaman mini-batch’lerle eğitilir. İleride bir sinir ağını SGD’yle eğiteceksin.

Kısa soruVeri kümesinde 1000 örnek var ve mini-batch boyutu 300. Bir epoch'ta kaç parametre güncellemesi yapılır?

Cevabı göster

4: üç tane 300'lük ve bir tane 100'lük mini-batch. Bir epoch'ta her örnek bir kez kullanılır. Son mini-batch küçük kalır ama yine bir güncellemedir.

Tek hamlede çözüm

Doğrusal regresyonda loss’un dibini bir formülle bulabilirsin. Çoğu modelde bu mümkün değildir. Gradyan dipte sıfırdır; o hâlde gradyanı sıfıra eşitleyen w\mathbf{w}‘yi bulmak yeter.

MSE çanağının tek bir dibi olduğunu varsayalım. Bir öznitelik başka bir özniteliğin kopyasıysa böyle değildir; buna aşağıda döneceğiz.

Bias sütunu

Önce bb‘yi ağırlıkların içine alalım:

  1. XX‘in sağına, her elemanı 1 olan bir sütun ekle. Yeni matrise X~=[X∣1]\tilde{X} = [X \mid \mathbf{1}] diyelim (kodda Xb).
  2. w\mathbf{w}‘nin sonuna bb‘yi ekle: w~=(w,b)\tilde{\mathbf{w}} = (\mathbf{w}, b).

Böylece tahmin y^=X~w~\hat{\mathbf{y}} = \tilde{X}\tilde{\mathbf{w}} olur. Gradyan formülü hem w\mathbf{w}‘yi hem bb‘yi kapsar; son elemanı bb‘nin gradyanıdır.

import numpy as npX = np.array([[40.], [60.], [80.], [100.]])Xb = np.hstack([X, np.ones((4, 1))])       # sağa birler sütunuprint(Xb.shape, Xb[0])                     # → (4, 2) [40.  1.]theta = np.array([0.2, 2.0])               # w ve b tek vektördeprint(Xb @ theta)                          # → [10. 14. 18. 22.]print(X @ np.array([0.2]) + 2.0)           # → [10. 14. 18. 22.]

Normal denklemler

Gradyanı sıfıra eşitle:

2N X~⊤(X~w~−y)=0⟹X~⊤X~ w~=X~⊤y\frac{2}{N}\,\tilde{X}^\top(\tilde{X}\tilde{\mathbf{w}} - \mathbf{y}) = \mathbf{0} \quad\Longrightarrow\quad \tilde{X}^\top\tilde{X}\,\tilde{\mathbf{w}} = \tilde{X}^\top\mathbf{y}

Bu doğrusal sisteme normal denklemler (normal equations) denir. İsim buradan gelir; ilk denklem, hata vektörünün X~\tilde{X} matrisinin her sütununa dik (normal) olduğunu söyler. X~⊤X~\tilde{X}^\top\tilde{X} matrisinin tersi varsa çözüm şudur:

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

Bu çözüme, hataların karelerinin toplamını en küçük yaptığı için en küçük kareler (least squares) çözümü denir.

import numpy as npX = np.array([[40.], [60.], [80.], [100.]])y = np.array([10., 15., 17., 22.])Xb = np.hstack([X, np.ones((4, 1))])A = Xb.T @ Xbc = Xb.T @ yprint(A.tolist())                  # → [[21600.0, 280.0], [280.0, 4.0]]print(c.tolist())                  # → [4860.0, 64.0]theta = np.linalg.solve(A, c)          # sistemi çözerror = Xb @ theta - y# hata, Xb'nin her sütununa dik: Xb.T @ error sıfırprint(np.allclose(Xb.T @ error, 0))    # → True

Elle çözüm

Dört ev için sistem küçüktür, elle çözebiliriz: 21600 w+280 b=486021600\,w + 280\,b = 4860 ve 280 w+4 b=64280\,w + 4\,b = 64.

  1. İkinci denklemden bb‘yi çöz: b=16−70 wb = 16 - 70\,w çıkar. Bu, vadinin tabanındaki doğrudur.
  2. Bunu birinci denkleme koy: 21600 w+280 (16−70 w)=486021600\,w + 280\,(16 - 70\,w) = 4860.
  3. Sadeleştir: 2000 w=3802000\,w = 380, yani w=0,19w = 0{,}19.
  4. bb‘yi bul: b=16−70⋅0,19=2,7b = 16 - 70 \cdot 0{,}19 = 2{,}7.

Bu, gradient descent’in ölçekli çanakta bulduğu çizgidir, ama tek hamlede.

Ters almak yerine sistemi çözmek

Ters almak yerine sistemi çöz. Formüldeki ters, hesaplamanın tarifi değildir:

  • Bir matrisin tersini almak, aynı sistemi çözmekten daha pahalıdır.
  • Ters, yuvarlama hatalarını çoğu zaman daha çok büyütür.

Bu yüzden normal denklemleri bir doğrusal sistem olarak çözersin, yukarıda eleyerek yaptığımız gibi. NumPy’da bunu np.linalg.solve yapar:

import numpy as npdef least_squares(X, y):    Xb = np.hstack([X, np.ones((X.shape[0], 1))])   # birler sütunu    theta = np.linalg.solve(Xb.T @ Xb, Xb.T @ y)    # tersini alma, çöz    return theta[:-1], float(theta[-1])X = np.array([[40.], [60.], [80.], [100.]])y = np.array([10., 15., 17., 22.])w, b = least_squares(X, y)print(np.round(w, 2), round(b, 2))                   # → [0.19] 2.7

Kütüphaneler bir adım daha ileri gider: np.linalg.lstsq normal denklemleri hiç kurmadan X~\tilde{X} matrisini doğrudan ayrıştırır. Bu, sayısal olarak daha sağlamdır.

Kapalı formun sınırları

Kapalı formun iki pratik sınırı var:

  • X~⊤X~\tilde{X}^\top\tilde{X} matrisinin tersi yoksa tek bir çözüm olmaz: aynı loss’u veren sonsuz sayıda çözüm bulunur. Örneğin bir öznitelik başka bir özniteliğin kopyasıdır: iki ağırlığın toplamı aynı kaldıkça tahminler değişmez. Örnek sayısının öznitelik sayısından az olması da aynı sonucu verir.
  • Öznitelik sayısı dd büyüdükçe d×dd \times d matris bellekte ve işlemde ağırlaşır; milyonlarca özniteliğe sığmaz.

Kapalı form ve gradient descent

Asıl sınır başka: kapalı form, doğrusal model ile MSE gibi birkaç özel durumda vardır. Şu durumlarda gradyanı sıfıra eşitleyen denklemin kapalı bir çözümü olmaz:

  • Çıktıyı 0 ile 1 arasına sıkıştıran sigmoid gibi doğrusal olmayan bir fonksiyondan geçirirsin.
  • Sinir ağlarına geçersin.

Gradient descent ise gradyanı hesaplanabilen her model ve loss’a uygulanabilir; ama en iyi parametreleri bulmayı her zaman garanti etmez. İki yolu da bilmek iyidir, ama sonraki derslerin aracı gradient descent’tir. Lojistik regresyon dersi aynı modele sigmoid ekler ve çıktıyı bir olasılığa çevirir.

Kısa soruİki feature birbirinin tam kopyası. Normal denklemler için ne söyleyebilirsin?

Cevabı göster

Matrisin tersi yoktur ve aynı loss'u veren sonsuz sayıda çözüm bulunur. Kopya sütun, matrisin sütunlarını doğrusal bağımlı yapar. Ters olmaz; weight'ler arasındaki paylaşım serbest kalır.

Özet

  • Standartlaştırma, öznitelikten ortalamasını çıkarıp standart sapmasına böler; çanağı yuvarlar ve aynı çizgiye çok daha çabuk varırsın.
  • Ölçekleme çözümü değiştirmez. Yeni bir örneği, eğitimdeki ortalama ve standart sapmayla ölçekle.
  • Mini-batch’ler gradyanı verinin bir parçasından yaklaşık hesaplar; bir epoch ⌈N/B⌉\lceil N / B \rceil güncelleme içerir.
  • SGD adımı ucuzlatır ama gürültülü yapar: batch küçüldükçe loss daha çok titrer.
  • Normal denklemler en küçük kareler çözümünü kapalı formda verir; sistemi çözersin, tersini almazsın.
  • Kapalı form yalnızca doğrusal model ile MSE gibi özel durumlarda vardır; gradient descent genel yöntemdir.

Terimler

TürkçeEnglish
öznitelikfeature
öznitelik ölçeklemefeature scaling
kapalı formclosed form
mini yığınmini-batch
öğrenme oranılearning rate
epochepoch
stokastik gradyan inişistochastic gradient descent, SGD
gradyan inişigradient descent
ağırlıkweight
sapmabias
kayıploss
ortalama kare hatamean squared error, MSE
veri kümesidataset
standartlaştırmastandardization
standart sapmastandard deviation
eş yükselti eğrilericontour lines
öğrenme eğrisilearning curve
yığınbatch
tohumseed
sinir ağıneural network
normal denklemlernormal equations
en küçük karelerleast squares

Şimdi sıra sende

  1. Mini yığınlara bölZorluk: OrtaBaşla →
  2. En küçük kareler çözümüZorluk: ZorBaşla →
İlk problem: Mini yığınlara böl →

Notların

Metinden bir bölüm seçip Not al'a basarak da ekleyebilirsin.

Geri bildirim

Tür