ML Temelleri · Modül 3 · 32 dakika

Nöronlar ve aktivasyonlar

Lojistik regresyon girdilerden bir skor hesaplar. Skoru sigmoid’den geçirir ve bir olasılık (probability) verir. Sigmoid, skoru 0 ile 1 arasına sıkıştıran fonksiyondur.

Bu küçük birime nöron (neuron) denir. Sinir ağı, nöronların yoğun katmanlar (dense layer)yoğun katman (dense layer): Her girdinin her nörona bağlandığı katman: girdiler bir ağırlık matrisiyle çarpılır, sapma eklenir ve sonuç bir aktivasyon fonksiyonundan geçer. hâlinde art arda dizilmesiyle kurulur. Böyle katmanlardan oluşan modele çok katmanlı algılayıcı (multilayer perceptron, MLP)çok katmanlı algılayıcı (multilayer perceptron, MLP): Aralarında aktivasyon fonksiyonu bulunan birkaç yoğun katmanın art arda dizilmesiyle kurulan sinir ağı. denir.

Sinir ağının işe yaraması için her nöronun sonunda doğrusal olmayan bir aktivasyon fonksiyonu (activation function)aktivasyon fonksiyonu (activation function): Bir nöronun ağırlıklı toplamını çıktıya çeviren ve ağın doğrusal olmayan fonksiyonlar öğrenebilmesini sağlayan fonksiyon; en bilinenleri sigmoid, tanh, ReLU ve GELU'dur. gerekir. Bu seçim eğitimi doğrudan etkiler. Bazı seçimlerde gradyan (gradient)gradyan (gradient): Bir fonksiyonun en hızlı arttığı yönü ve bu artışın dikliğini gösteren türev vektörü. katmanlardan geçerken erir; buna kaybolan gradyan (vanishing gradient)kaybolan gradyan (vanishing gradient): Gradyanın, türevi küçük aktivasyonlardan geçerek girişe yakın katmanlara ulaşana kadar neredeyse sıfıra inmesi; o katmanlar bu yüzden çok yavaş öğrenir ya da hiç öğrenemez. (vanishing gradient) denir.

Bu derste şunları yapacaksın:

  • Nöronu kuracaksın.
  • Katmanları (layer) aktivasyon olmadan üst üste koymanın neden bir şey kazandırmadığını göreceksin.
  • Dört aktivasyon fonksiyonunu ve türevlerini (derivative) karşılaştıracaksın.
  • KK sınıflı bir problem için ham skorları (logits) olasılığa çeviren softmax’ı öğreneceksin.
  • Çapraz entropiyi (cross-entropy) öğreneceksin.

Bir nöron

Nöron, ağırlıklı bir toplam ve ardından bir aktivasyon fonksiyonudur.

Ağırlıklı toplam ve aktivasyon

Lojistik regresyonun iki adımı vardı:

  1. Ağırlıklı toplamı hesapla: girdileri kendi ağırlıklarıyla (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. çarp, topla ve sapmayı (bias)sapma (bias): Girdilerle çarpılmadan çıktıya eklenen parametre. Doğrusal modelde bütün öznitelikler 0 iken modelin verdiği tahmindir. ekle.
  2. Çıkan skoru sigmoid’e ver.

Aynı iki adım bir nöronun tanımıdır. x\mathbf{x} girdi vektörü, w\mathbf{w} weight’ler ve bb bias olsun:

z=w⊤x+b,a=f(z)z = \mathbf{w}^\top \mathbf{x} + b, \qquad a = f(z)

Denklemler iki şey tanımlar:

  • Birinci denklem ağırlıklı toplamı verir. zz‘ye aktivasyon öncesi değer (pre-activation) diyeceğiz.
  • İkinci denklem zz‘yi bir ff fonksiyonundan geçirir. Çıktı aa‘dır ve aktivasyon sonrası değer (activation) adını taşır.

ff sigmoid olduğunda ortaya lojistik regresyon çıkar. Başka bir ff seçersen başka bir nöron elde edersin.

Aşağıda girdileri, weight’leri ve bias’ı kaydır, aktivasyonu değiştir. Başlangıç değerleri örneğimizdir; üçüncü girdi 0’dır.

neuron_diagram

Nöron · neuron · z = wᵀx + b, a = f(z)

bf: sigmoidx₁2x₂−1x₃0nöronz = 1,5z = w₁x₁ + w₂x₂ + w₃x₃ + b= 3 + (−2) + 0 + 0,5 = 1,5a0,818a = f(z)w₁·x₁ = 1,5 · 2 = 3w₂·x₂ = 2 · (−1) = −2w₃·x₃ = 1 · 0 = 0b = 0,5

z = w @ x + b# [1.5, 2.0, 1.0] @ [2.0, -1.0, 0.0] + 0.5 → 1.5

a = σ(z) = σ(1,5) = 0,818 0 ile 1 arasında, olasılık gibi okunur: %81,8

Sayılarla kuralım: x=(2; −1)\mathbf{x} = (2;\ -1), w=(1,5; 2)\mathbf{w} = (1{,}5;\ 2) ve b=0,5b = 0{,}5 olsun.

  1. Ağırlıklı toplamı hesapla: z=1,5⋅2+2⋅(−1)+0,5=1,5z = 1{,}5 \cdot 2 + 2 \cdot (-1) + 0{,}5 = 1{,}5.
  2. Sigmoid’i uygula: σ(1,5)≈0,818\sigma(1{,}5) \approx 0{,}818.

Nöron bu girdinin 1 sınıfından olduğuna %81,8 olasılık veriyor.

Aynı hesap kodda iki satırdır. f aktivasyon fonksiyonunu parametre olarak alır:

import numpy as npdef sigmoid(z):    return 1 / (1 + np.exp(-z))def neuron(x, w, b, f):    z = w @ x + b     # ağırlıklı toplam    return f(z)       # aktivasyonx = np.array([2.0, -1.0])w = np.array([1.5, 2.0])b = 0.5print(w @ x + b)                 # → 1.5print(neuron(x, w, b, sigmoid))  # → 0.8175744761936437

Aktivasyonsuz nöronlar tek nörona çöker

ff olmasa nöron yalnızca zz değerini verirdi. Bu, doğrusal bir modeldir. Asıl sorun, nöronlar art arda dizildiğinde ortaya çıkar.

Tek girdili iki nöronu aktivasyonsuz birbirine bağlayalım. Birincisi z1=2x+1z_1 = 2x + 1, ikincisi z2=3z1−4z_2 = 3z_1 - 4 olsun.

  1. Birincinin çıktısını ikincide yerine koy: z2=3(2x+1)−4z_2 = 3(2x + 1) - 4.
  2. Parantezi aç: z2=6x−1z_2 = 6x - 1.

Sonuç yine bir doğrudur. Genel olarak:

w2(w1x+b1)+b2=(w2w1) x+(w2b1+b2)w_2(w_1 x + b_1) + b_2 = (w_2 w_1)\,x + (w_2 b_1 + b_2)

Sol taraf iki doğrusal adım, sağ taraf tek bir doğrusal adımdır: yeni weight w2w1w_2 w_1, yeni bias w2b1+b2w_2 b_1 + b_2.

Aynı sayılarla kodda iki yolu karşılaştır:

w1, b1 = 2.0, 1.0w2, b2 = 3.0, -4.0x = np.array([-1.0, 0.0, 1.0, 2.0])two_steps = w2 * (w1 * x + b1) + b2one_step = (w2 * w1) * x + (w2 * b1 + b2)two_steps                # → array([-7., -1.,  5., 11.])one_step                 # → array([-7., -1.,  5., 11.])(w2 * w1, w2 * b1 + b2)  # → (6.0, -1.0)

Doğrusal adımları ne kadar üst üste koyarsan koy, sonuç tek bir doğrusal adımdır. Aktivasyonsuz yüz layer’lık bir ağ, tek bir layer’dan daha güçlü değildir.

ReLU ile kırılma

Araya bir aktivasyon koyunca bu değişir. Negatif değerleri 0’a çeken max⁡(0,z)\max(0, z) fonksiyonunu seçelim. Adı ReLU’dur; ayrıntısına aşağıda bakacağız.

İlk nöronun çıktısı a1=max⁡(0,z1)a_1 = \max(0, z_1) olsun. İkinci nöron z2=3a1−4z_2 = 3a_1 - 4 hesaplasın:

xxz1=2x+1z_1 = 2x + 1Aktivasyonsuz: 3z1−43z_1 - 4a1=max⁡(0,z1)a_1 = \max(0, z_1)ReLU’lu: 3a1−43a_1 - 4
−1−1−70−4
01−11−1
13535
2511511

Tablonun son sütununu ve sütunlardaki farkları kodla bul:

# önceki bloktaki np, w1, b1 ve x ilez1 = w1 * x + b1z2 = 3 * z1 - 4         # aktivasyonsuza1 = np.maximum(0, z1)  # ReLU: negatifleri 0 yapz2_relu = 3 * a1 - 4    # ReLU'luz2_relu                 # → array([-4., -1.,  5., 11.])np.diff(z2)             # → array([6., 6., 6.])np.diff(z2_relu)        # → array([3., 6., 6.])

Komşu satırlar arasındaki farka bak:

  • Aktivasyonsuz sütunda fark hep 6’dır. Bu, 6x−16x - 1 doğrusunun eğimidir.
  • ReLU’lu sütunda farklar 3, 6, 6 çıkar. x≤−0,5x \le -0{,}5 iken çıktı sabit −4-4‘tür, ondan sonra 6x−16x - 1 olur.

Eğim bir noktada 0’dan 6’ya geçiyor ve bir doğru bunu yapamaz.

Evrensel yaklaşım teoremi

Tek bir kırılma küçük görünür. Çok sayıda nöronun kırılmaları birleşince ağ eğri fonksiyonlara yaklaşabilir.

Bu kanıtlanmış bir sonuçtur. Tek bir gizli katmanı (hidden layer) olan bir ağ, sınırlı bir aralıkta sürekli her fonksiyona istenen kadar yaklaşabilir. İki koşul gerekir:

  • Aktivasyon fonksiyonu polinom değildir. ReLU, sigmoid ve tanh bu koşulu sağlar.
  • Gizli katmanda yeterince nöron vardır.

Teoreme evrensel yaklaşım teoremi (universal approximation theorem) denir. Bu derste kanıtına girmiyoruz.

Küçük bir deneyle görelim. [0; 1][0;\ 1] aralığında x2x^2 eğrisine nn ReLU nöronuyla yaklaşacağız. h=1/nh = 1/n olsun:

  1. k=0,1,…,n−1k = 0, 1, \ldots, n-1 için bir nöron max⁡(0, x−kh)\max(0,\ x - kh) hesaplar. Weight’i 1, bias’ı −kh-kh‘dir; x=khx = kh‘de kırılır.
  2. Nöronların çıktılarını birer weight ile çarpıp topla. Bunlar, yukarıdaki z2=3a1−4z_2 = 3a_1 - 4 hesabındaki 3 gibi çıkış weight’leridir.
  3. Her çıkış weight’i, kendi kırılma noktasından sonra eğimi o kadar artırır. x2x^2‘yi 0,h,2h,…0, h, 2h, \ldots noktalarında birleştiren kırık çizginin eğimleri h,3h,5h,…h, 3h, 5h, \ldots olur. Bu yüzden ilk weight hh, ötekiler 2h2h‘dir.
def approx(x, n):    h = 1 / n    out = 0.0    for k in range(n):        w_out = h if k == 0 else 2 * h   # eğim artışı: önce h, sonra 2h        out = out + w_out * np.maximum(0, x - k * h)  # kh'de kırılan ReLU nöronu    return outx = np.linspace(0, 1, 4097)for n in (4, 8, 16):    print(n, np.abs(approx(x, n) - x ** 2).max())# → 4 0.015625# → 8 0.00390625# → 16 0.0009765625

Bu deneyde nöron sayısı iki katına çıkınca en büyük hata dörtte birine düşer. Weight’leri burada elle seçtik. Teorem böyle weight’lerin var olduğunu söyler; onları veriden bulmak eğitimin işidir.

Kısa sorux = (2; -1), w = (1,5; 2) ve b = -2 olan bir nöron ReLU kullanıyor. Çıktı a kaç olur?

Cevabı göster

0 z = 1,5 · 2 + 2 · (-1) + (-2) = -1 negatiftir. ReLU negatif değeri 0'a çeker, yani a = 0.

Dört aktivasyon fonksiyonu

Pratikte dört aktivasyon fonksiyonu sık karşına çıkar: sigmoid, tanh, ReLU ve GELU.

Fonksiyonlar ve türevleri

Eğitimde gradyan bu fonksiyonlardan geçer. Bu yüzden her birinin türevine de ihtiyacımız var. Fonksiyonlar şunlardır:

σ(z)=11+e−ztanh⁡(z)=ez−e−zez+e−zReLU(z)=max⁡(0,z)GELU(z)=z Φ(z)\begin{aligned} \sigma(z) &= \frac{1}{1 + e^{-z}} \\ \tanh(z) &= \frac{e^{z} - e^{-z}}{e^{z} + e^{-z}} \\ \mathrm{ReLU}(z) &= \max(0, z) \\ \mathrm{GELU}(z) &= z\,\Phi(z) \end{aligned}

Türevleri ve özellikleri yan yana koyalım:

FonksiyonNe yaparTürev f′(z)f'(z)Not
sigmoidGirdiyi 0 ile 1 arasına sıkıştırır.σ(z)(1−σ(z))\sigma(z)\bigl(1 - \sigma(z)\bigr)En büyük değeri z=0z = 0‘daki 0,250{,}25‘tir.
tanh (hiperbolik tanjant)Girdiyi −1-1 ile 1 arasına sıkıştırır; 0 etrafında simetriktir.1−tanh⁡2(z)1 - \tanh^2(z)z=0z = 0‘da 1’dir.
ReLUNegatifleri 0’a çeker, pozitifleri olduğu gibi geçirir.z>0z > 0 için 1, z<0z < 0 için 0z=0z = 0‘da eğim tanımsızdır; uygulamada 0 alınır ve biz de öyle yapacağız.
GELUReLU’nun köşesini yumuşatır.Φ(z)+z φ(z)\Phi(z) + z\,\varphi(z)Transformer’larda yaygındır.

Φ(z)\Phi(z), standart normal dağılımdan çekilen bir sayının zz değerinden küçük olma olasılığıdır. zz büyükken 1’e, çok negatifken 0’a yakındır. φ\varphi ise standart normal dağılımın yoğunluğudur.

Üç fonksiyonu ve türevlerini kodla yaz. relu_grad, z=0z = 0 için 0 verir:

import numpy as npdef sigmoid(z):    return 1 / (1 + np.exp(-z))def sigmoid_grad(z):    s = sigmoid(z)    return s * (1 - s)def tanh_grad(z):    return 1 - np.tanh(z) ** 2def relu(z):    return np.maximum(0, z)def relu_grad(z):    return (z > 0).astype(float)    # z = 0 için 0z = np.array([-2.0, 0.0, 3.0])np.round(sigmoid(z), 3)         # → array([0.119, 0.5  , 0.953])np.round(sigmoid_grad(z), 3)    # → array([0.105, 0.25 , 0.045])np.round(np.tanh(z), 3)         # → array([-0.964,  0.   ,  0.995])np.round(tanh_grad(z), 3)       # → array([0.071, 1.   , 0.01 ])relu(z)                         # → array([0., 0., 3.])relu_grad(z)                    # → array([0., 0., 1.])

GELU için Φ\Phi ve φ\varphi gerekir. Φ\Phi‘yi standart kütüphanedeki erf ile yazarız:

from math import erf, sqrt, pidef cdf(z):    # Φ(z) = 0.5 * (1 + erf(z / sqrt(2)))    return 0.5 * (1 + np.vectorize(erf)(z / sqrt(2)))def pdf(z):    # φ(z): standart normal yoğunluk    return np.exp(-z ** 2 / 2) / sqrt(2 * pi)def gelu(z):    return z * cdf(z)def gelu_grad(z):    return cdf(z) + z * pdf(z)z = np.array([-1.0, 1.0])np.round(gelu(z), 2)        # → array([-0.16,  0.84])np.round(gelu_grad(z), 2)   # → array([-0.08,  1.08])

Grafikte fonksiyon ve türev

Aşağıda bir fonksiyon seç ve imleci zz ekseni boyunca sürükle. İmlecin altında f(z)f(z) ile f′(z)f'(z) değerini okursun. Türevin sıfıra çok yaklaştığı, yani −0,1-0{,}1 ile 0,10{,}1 arasında kaldığı bölgeler “gradyan kayboluyor” diye gölgelenir.

activation_explorer

Aktivasyon ve türev · activation & derivative · f(z) ve f'(z)

-6-4-2024600,250,51zf(z)f'(z)gradyan kayboluyor

sigmoid · z = 0,00 · f(z) = 0,500 · f'(z) = 0,250

Şunları dene:

  • Sigmoid’de imleci z=0z = 0‘a koy: f=0,5f = 0{,}5 ve f′=0,25f' = 0{,}25. Sonra z=3z = 3‘e sürükle: f≈0,95f \approx 0{,}95 iken f′≈0,045f' \approx 0{,}045. Fonksiyon hâlâ 1’e varmadı, ama eğrisi neredeyse yatay.
  • tanh için z=0z = 0‘da türev 1, z=2z = 2‘de yaklaşık 0,070{,}07‘dir. Gölgeli bölge sigmoid’dekinden biraz erken başlar. Sigmoid’de ∣z∣>2,06\lvert z \rvert > 2{,}06 civarında, tanh’ta ∣z∣>1,82\lvert z \rvert > 1{,}82 civarında.
  • ReLU’da z=1z = 1 için f=1f = 1 ve f′=1f' = 1; z=−1z = -1 için ikisi de 0’dır. Gölgeli bölge, zz‘nin negatif tarafının tamamıdır.
  • GELU’da üç şeye bak:
    • z=1z = 1 için f≈0,84f \approx 0{,}84 ve f′≈1,08f' \approx 1{,}08. Türev 1’i aşabilir.
    • z=−1z = -1 için f≈−0,16f \approx -0{,}16 ve f′≈−0,08f' \approx -0{,}08. GELU küçük negatif değerler verebilir ve türevi negatif olabilir. ReLU’da böyle bir şey yoktur.
    • GELU’nun negatif tarafında gölgesiz dar bir bant kalır. Türev orada −0,1-0{,}1 ile −0,13-0{,}13 arasındadır; küçük ama sıfır değildir.

Gölgenin başladığı zz değerini kodla da bulabilirsin. Türevin ilk kez 0,10{,}1‘in altına indiği noktaya bak:

# önceki bloklardaki sigmoid_grad ve tanh_grad ilez = np.linspace(0, 5, 5001)print(z[sigmoid_grad(z) < 0.1][0])  # → 2.064print(z[tanh_grad(z) < 0.1][0])     # → 1.819

Vanishing gradient

Gölgeli bölgelerin önemi geri yayılımdan gelir. Bir sonraki derste göreceğin geri yayılımda (backpropagation)geri yayılım (backpropagation): Zincir kuralıyla, kaybın her parametreye göre gradyanını çıkıştan girişe doğru hesaplama yöntemi., kaybın (loss) gradyanı çıkıştan girişe doğru taşınır. Gradyan her layer’dan geçerken o layer’ın aktivasyon türeviyle çarpılır.

Türev küçükse gradyan küçülür. Bir layer’da küçülmesi önemsizdir, ama küçülmeler çarpıldıkça birikir.

Tipik bir layer’da sigmoid’in türevi yaklaşık 0,20{,}2‘dir. Örnek olarak z=1z = 1 alalım. Orada σ′(1)≈0,197\sigma'(1) \approx 0{,}197 ve tanh⁡′(1)≈0,420\tanh'(1) \approx 0{,}420 olur.

Weight’leri 1 sayarsak her layer gradyanı türevle çarpar. On sigmoid layer’dan sonra çarpım 0,19710≈9⋅10−80{,}197^{10} \approx 9 \cdot 10^{-8} olur. Girişe ulaşan gradyan, çıkıştakinin on milyonda biri kadardır.

Sigmoid’in türevi en iyi ihtimalle 0,250{,}25‘tir (z=0z = 0‘da). Gerçek bir ağda zz genelde 0’da durmaz, bu yüzden çarpan daha da küçüktür.

Kodda z=1z = 1 ile izle:

# önceki bloktaki sigmoid_grad ve tanh_grad ileprint(sigmoid_grad(1.0))    # → 0.19661193324148185print(tanh_grad(1.0))       # → 0.41997434161402614grad = 1.0for layer in range(1, 11):    grad = grad * sigmoid_grad(1.0)  # her layer'da türevle çarp    if layer in (1, 5, 10):        print(layer, grad)# → 1 0.19661193324148185# → 5 0.0002937983603191941# → 10 8.631747652624701e-08

Gradyan weight’lerle de çarpılır. Bu hesap yalnızca aktivasyonların etkisini gösterir. Yine de sigmoid’le gradyan her layer’da erimeye yatkındır.

Aktivasyonu seç ve katman sayısını sürükle.

vanishing_gradient

gradyan = f′(z)ᵏ · her katmanda tipik ön-aktivasyon z = 1, ağırlıklar 1 alındı

110⁻²10⁻⁴10⁻⁶10⁻⁸ReLU: 110,220,03938·10⁻³41·10⁻³53·10⁻⁴66·10⁻⁵78910gradyanın büyüklüğü · log eksenikayıptan geriye k. katman

sigmoid′(1) = 0,197 · 6 katman geriye: ilk katmana ulaşan gradyan 0,197⁶ = 5,8·10⁻⁵ en iyi durumda bile (z = 0) türev yalnızca 0,25: 0,25⁶ = 2,4·10⁻⁴

Gradyanın giriş tarafına ulaşamadan erimesine vanishing gradientkaybolan gradyan (vanishing gradient): Gradyanın, türevi küçük aktivasyonlardan geçerek girişe yakın katmanlara ulaşana kadar neredeyse sıfıra inmesi; o katmanlar bu yüzden çok yavaş öğrenir ya da hiç öğrenemez. denir. Girişe yakın layer’ların weight’leri neredeyse hiç güncellenmez ve ağ öğrenmez.

Yukarıdaki grafikte sigmoid ve tanh’ın gölgeli bölgelerinin iki uçta olduğuna dikkat et. zz bu uçlara düştüğünde nöronun çıktısı neredeyse hiç değişmez ve gradyan geçmez.

ReLU ve dying ReLU

ReLU bu yüzden yaygınlaştı. Pozitif tarafta türevi tam 1’dir ve gradyan olduğu gibi geçer. Bedeli negatif taraftır.

Bir nöronun zz değeri her girdi için negatifse, türevi hep 0’dır. Gradyan o nöronun weight’lerini hiç güncellemez ve nöron öğrenmeyi bırakır. Buna dying ReLU denir (ölen ReLU).

Örnek: b=−10b = -10 olan bir nöron, üç girdinin hepsinde negatif zz verir:

# önceki bloktaki relu_grad ileX = np.array([[1.0, 2.0], [2.0, 1.0], [3.0, 0.5]])w = np.array([1.0, 1.0])b = -10.0z = X @ w + b       # üç girdi için aktivasyon öncesi değerz                   # → array([-7. , -7. , -6.5])relu_grad(z)        # → array([0., 0., 0.])

GELU’nun türevi

GELU’nun türevi de ReLU gibi pozitif tarafta 1’e yaklaşır. Köşesi yoktur. zz çok negatif olmadıkça sıfırdan farklı, küçük bir türev verir.

# önceki bloktaki gelu_grad ilez = np.array([-6.0, -3.0, -1.0, 0.0, 1.0, 3.0])np.round(gelu_grad(z), 3)   # → array([-0.   , -0.012, -0.083,  0.5  ,  1.083,  1.012])
Kısa soruDerin bir ağın her layer'ında sigmoid kullanılıyor. Girişe yakın layer'lar neden neredeyse hiç öğrenmez?

Cevabı göster

Gradyan her layer'da sigmoid türeviyle çarpılır. Türev en çok 0,25 olduğundan gradyan girişe ulaşana kadar erir. Türev en iyi durumda bile 0,25'tir. On layer'dan sonra çarpım en çok 0,25 üzeri 10 olur, yani yaklaşık milyonda bir. Girişe yakın layer'ların weight'leri neredeyse hiç güncellenmez.

Layer’lar ve MLP

Bir layer, aynı girdiyi gören nöronların yan yana dizilmesidir.

Dense layer

doutd_{\text{out}} nöronlu bir layer’da her nöronun kendi weight vektörü ve kendi bias’ı vardır.

Hesabı matrislere şöyle dökeriz:

  1. Weight vektörlerini WW matrisinin sütunlarına koy.
  2. Bias’ları b\mathbf{b} vektörüne koy.
  3. NN örnekten (sample) oluşan bir yığını (batch) XX matrisine koy. XX‘in her satırı bir örnektir.

Böylece bütün nöronların ağırlıklı toplamı tek bir matris çarpımı (matrix product) olur:

Z=XW+b,A=f(Z)Z = XW + \mathbf{b}, \qquad A = f(Z)

Formül her örneği her nöronun weight’iyle çarpıp toplar, bias’ı ekler ve aktivasyonu uygular. Şekiller (shape) şöyledir:

  • XX: N×dinN \times d_{\text{in}}.
  • WW: din×doutd_{\text{in}} \times d_{\text{out}}.
  • b\mathbf{b}: doutd_{\text{out}} elemanlı.
  • ZZ ve AA: N×doutN \times d_{\text{out}}.

Matris çarpımında soldakinin sütun sayısı sağdakinin satır sayısına eşit olmalıdır. Bu yüzden dind_{\text{in}} iki yanda da aynı kalır.

Layer b\mathbf{b}‘yi her satıra ekler. NumPy’da buna yayınlama (broadcasting) denir. Layer aktivasyonu ZZ matrisinin her elemanına ayrı ayrı, yani eleman bazında (element-wise), uygular.

Her girdinin her nörona bağlandığı bu layer’a dense layeryoğun katman (dense layer): Her girdinin her nörona bağlandığı katman: girdiler bir ağırlık matrisiyle çarpılır, sapma eklenir ve sonuç bir aktivasyon fonksiyonundan geçer. denir.

Bir gizli nörona tıkla ve onun weight’lerinin WW‘nin hangi sütunu olduğuna bak.

layer_diagram

Dense layer · dense layer · Z = XW + b

h₁h₂h₃h₄x₁x₂x₃y₁y₂−0,80,40,7b = −0,2girdi · 3hidden layer · 4çıktı · 2

W(1) · (d_in, d_out) = (3, 4)

x₁
0,5
−0,8
0,3
1
x₂
−0,6
0,4
0,9
−0,2
x₃
0,2
0,7
−0,5
0,6
b
0,1
−0,2
0,3
0
W⁽¹⁾ şekli(3, 4)d_in × d_out = 3 × 4
parametre · W + b163·4 + 4 = 12 + 4
bütün ağ · iki layer2616 + (4·2 + 2) = 16 + 10
gizli nöron4 nöron

W.shape, b.shape # → ((3, 4), (4,))W[:, 1] # → [-0.8, 0.4, 0.7]

h₂ nöronu girdiyi 3 weight ile görür: W[:, 1] = (−0,8; 0,4; 0,7) · bias b₂ = −0,2 bir nöron: 3 weight + 1 bias = 4 parametre · 4 nöron yan yana: 4 · 4 = 16

Küçük bir örnek: 2 örnek, 3 girdi, 2 nöron.

X=[120−112],W=[1−11211],b=[0,5−4]X = \begin{bmatrix} 1 & 2 & 0 \\ -1 & 1 & 2 \end{bmatrix},\quad W = \begin{bmatrix} 1 & -1 \\ 1 & 2 \\ 1 & 1 \end{bmatrix},\quad \mathbf{b} = \begin{bmatrix} 0{,}5 & -4 \end{bmatrix} Z=XW+b=[3325]+[0,5−4]=[3,5−12,51]Z = XW + \mathbf{b} = \begin{bmatrix} 3 & 3 \\ 2 & 5 \end{bmatrix} + \begin{bmatrix} 0{,}5 & -4 \end{bmatrix} = \begin{bmatrix} 3{,}5 & -1 \\ 2{,}5 & 1 \end{bmatrix}

XX matrisi 2×32 \times 3, WW matrisi 3×23 \times 2‘dir. Ortadaki 3’ler eşleşir ve sonuç 2×22 \times 2 olur.

ff ReLU ise negatif eleman sıfırlanır. AA‘nın ilk satırı (3,5; 0)(3{,}5;\ 0), ikincisi (2,5; 1)(2{,}5;\ 1) olur.

Aynı örnek NumPy’da tek satırlık bir matris çarpımıdır:

import numpy as npX = np.array([[1.0, 2.0, 0.0],              [-1.0, 1.0, 2.0]])W = np.array([[1.0, -1.0],              [1.0, 2.0],              [1.0, 1.0]])b = np.array([0.5, -4.0])Z = X @ W + b                 # b her satıra yayınlanırZ# → array([[ 3.5, -1. ],#          [ 2.5,  1. ]])A = np.maximum(0, Z)          # ReLU, eleman bazındaA# → array([[3.5, 0. ],#          [2.5, 1. ]])(X.shape, W.shape, Z.shape)   # → ((2, 3), (3, 2), (2, 2))

Not

PyTorch’un nn.Linear layer’ı weight matrisini bu derstekinin devriği (transpose) olarak, (d_out, d_in) biçiminde saklar. Çarpmayı X @ W.T olarak yapar. Bu derste ve problemlerde WW hep (d_in, d_out) biçimindedir.

Aktivasyonsuz layer’lar çöker

Aktivasyonsuz nöronların çökmesi, layer’lar için de geçerlidir. Aktivasyon yoksa iki dense layer tek bir layer’a çöker:

(XW(1)+b(1)) W(2)+b(2)=X (W(1)W(2))+(b(1)W(2)+b(2))(XW^{(1)} + \mathbf{b}^{(1)})\,W^{(2)} + \mathbf{b}^{(2)} = X\,(W^{(1)}W^{(2)}) + (\mathbf{b}^{(1)}W^{(2)} + \mathbf{b}^{(2)})

Sağ taraftaki tek layer’ın weight matrisi W(1)W(2)W^{(1)}W^{(2)}, bias’ı b(1)W(2)+b(2)\mathbf{b}^{(1)}W^{(2)} + \mathbf{b}^{(2)}‘dir. Üst indis hangi layer’ın olduğunu gösterir.

Rastgele sayılarla iki tarafı karşılaştır:

rng = np.random.default_rng(0)X = rng.normal(size=(4, 3))W1, b1 = rng.normal(size=(3, 5)), rng.normal(size=5)W2, b2 = rng.normal(size=(5, 2)), rng.normal(size=2)two_layers = (X @ W1 + b1) @ W2 + b2one_layer = X @ (W1 @ W2) + (b1 @ W2 + b2)np.allclose(two_layers, one_layer)   # → True(W1 @ W2).shape                      # → (3, 2)

MLP

Bu yüzden layer’ların arasına aktivasyon koyarız. Dense layer’lardan ve aralarındaki aktivasyonlardan oluşan ağa MLPçok katmanlı algılayıcı (multilayer perceptron, MLP): Aralarında aktivasyon fonksiyonu bulunan birkaç yoğun katmanın art arda dizilmesiyle kurulan sinir ağı. denir. Girdi ile çıkış arasında kalan layer’lara gizli katman (hidden layer) denir.

Sınıflandırma (classification) için tipik bir MLP’ye bakalım. 8×8’lik bir rakam görüntüsü 64 öznitelik (feature) verir. Ağın 32 nöronlu bir hidden layer’ı ve 10 sınıfı olsun:

A=f(XW(1)+b(1)),Z=A W(2)+b(2)A = f\bigl(XW^{(1)} + \mathbf{b}^{(1)}\bigr), \qquad Z = A\,W^{(2)} + \mathbf{b}^{(2)}
Shape
XX(N,64)(N, 64)
W(1)W^{(1)}, b(1)\mathbf{b}^{(1)}(64,32)(64, 32), (32,)(32,)
AA(N,32)(N, 32)
W(2)W^{(2)}, b(2)\mathbf{b}^{(2)}(32,10)(32, 10), (10,)(10,)
ZZ(N,10)(N, 10)

Son layer’a aktivasyon koymayız: ZZ, her örnek için 10 logit’tir.

Bu ağın parametre (parameter) sayısı 64⋅32+32+32⋅10+10=241064 \cdot 32 + 32 + 32 \cdot 10 + 10 = 2410‘dur. Kodda her layer’ın W ve b boyutlarını topla:

d_in, d_hidden, d_out = 64, 32, 10n_params = d_in * d_hidden + d_hidden + d_hidden * d_out + d_outprint(n_params)   # → 2410

İleri geçiş

Girdiden çıktıya giden bu hesaba ileri geçiş (forward pass) denir. Kodda üç satırdır:

def forward(X, W1, b1, W2, b2):    Z1 = X @ W1 + b1          # ilk layer'ın ağırlıklı toplamı, (n, 32)    A1 = np.maximum(0, Z1)    # aktivasyon, Z1'in her elemanına ayrı ayrı    logits = A1 @ W2 + b2     # son layer, aktivasyonsuz, (n, 10)    return logitsrng = np.random.default_rng(0)X = rng.normal(size=(5, 64))W1, b1 = rng.normal(size=(64, 32)), np.zeros(32)W2, b2 = rng.normal(size=(32, 10)), np.zeros(10)forward(X, W1, b1, W2, b2).shape                 # → (5, 10)sum(p.size for p in (W1, b1, W2, b2))            # → 2410

Weight’lerin nasıl öğrenildiğini Geri yayılım dersinde göreceksin. Ardından ileride bir MLP’yi eğitmeye geçeceğiz.

Kısa soruİki dense layer'ın arasında aktivasyon fonksiyonu yoksa bu ikili neye denktir?

Cevabı göster

Tek bir dense layer'a. İki doğrusal adımın birleşimi yine doğrusaldır: iki weight matrisi çarpılıp tek bir matris, iki bias de birleşip tek bir bias olur.

Softmax

Softmax, KK skoru KK sınıf üzerinde bir olasılık dağılımına çevirir.

Softmax formülü

Sınıf sayısı KK olduğunda son layer KK logit verir. Lojistik regresyonda tek bir skoru sigmoid’le olasılığa çeviriyordun; KK sınıfta aynı işi softmax yapar.

Adı, “en büyüğünü seç” işlemini yumuşatmasından gelir. En büyük skora en büyük olasılığı verir, ama diğerlerini sıfırlamaz.

pk=ezk∑j=0K−1ezjp_k = \frac{e^{z_k}}{\sum_{j=0}^{K-1} e^{z_j}}

Üstel fonksiyon her skoru pozitif yapar. Bölme toplamı 1’e getirir. Sonuç, KK sınıf üzerinde bir olasılık dağılımıdır.

z=(2; 1; 0)\mathbf{z} = (2;\ 1;\ 0) için hesap şöyledir:

  1. Skorların üstelini al: e2≈7,389e^2 \approx 7{,}389, e1≈2,718e^1 \approx 2{,}718 ve e0=1e^0 = 1.
  2. Üstelleri topla: 11,10711{,}107.
  3. Her üsteli toplama böl: p≈(0,665; 0,245; 0,090)\mathbf{p} \approx (0{,}665;\ 0{,}245;\ 0{,}090).

Olasılıkların oranı yalnızca skorların farkına bağlıdır: pa/pb=eza−zbp_a / p_b = e^{z_a - z_b}. Skorlar arasındaki fark 1 arttığında oran e≈2,72e \approx 2{,}72 katına çıkar.

Aynı üç adım kodda üç satırdır:

import numpy as npz = np.array([2.0, 1.0, 0.0])exps = np.exp(z)exps                   # → array([7.3890561 , 2.71828183, 1.        ])print(exps.sum())      # → 11.107337927389695probs = exps / exps.sum()probs                  # → array([0.66524096, 0.24472847, 0.09003057])print(probs[0] / probs[1])   # → 2.7182818284590455

İki sınıfta softmax

K=2K = 2 olduğunda softmax sigmoid’e iner:

p1=ez1ez0+ez1=σ(z1−z0)p_1 = \frac{e^{z_1}}{e^{z_0} + e^{z_1}} = \sigma(z_1 - z_0)

Yani iki sınıfta softmax, skorların farkına uygulanan sigmoid’dir.

İki skor seç ve iki yolla hesapla. Fark 1,5 olduğundan sonuç, nöron örneğindeki 0,8180{,}818‘dir:

# önceki bloklardaki np ve sigmoid ilez0, z1 = 0.3, 1.8exps = np.exp([z0, z1])print(np.round(exps[1] / exps.sum(), 4))   # → 0.8176print(np.round(sigmoid(z1 - z0), 4))       # → 0.8176

Taşma

Büyük skorlarda üstel fonksiyon taşar. z=(1000; 1001; 1002)\mathbf{z} = (1000;\ 1001;\ 1002) olsun. e1000e^{1000} 64 bitlik bir float’a sığmaz.

exe^{x} ancak x≈709,78x \approx 709{,}78‘e kadar temsil edilir. Taşma (overflow) olur, sonuç inf çıkar ve inf / inf nan verir.

z = np.array([1000.0, 1001.0, 1002.0])# exp taşar; NumPy RuntimeWarning verirnp.exp(z)                      # → array([inf, inf, inf])np.exp(z) / np.exp(z).sum()    # → array([nan, nan, nan])

En büyüğünü çıkarmak

Çıkış yolu softmax’ın bir özelliğindedir. Her skora aynı cc sayısını eklersen ece^{c} pay ve paydadan sadeleşir ve olasılıklar değişmez.

ezk+c∑jezj+c=ec ezkec∑jezj=ezk∑jezj\frac{e^{z_k + c}}{\sum_j e^{z_j + c}} = \frac{e^{c}\,e^{z_k}}{e^{c}\sum_j e^{z_j}} = \frac{e^{z_k}}{\sum_j e^{z_j}}

Burada c=−mc = -m seçelim, mm skorların en büyüğü olsun. Örneğimizde m=1002m = 1002 olur:

  1. Skorlardan mm‘yi çıkar: (−2; −1; 0)(-2;\ -1;\ 0).
  2. Üstelleri al: (0,135; 0,368; 1)(0{,}135;\ 0{,}368;\ 1).
  3. Üstelleri topla: 1,5031{,}503.
  4. Her üsteli toplama böl: (0,090; 0,245; 0,665)(0{,}090;\ 0{,}245;\ 0{,}665).

Bu sonuç, (2; 1; 0)(2;\ 1;\ 0) örneğinin tersten yazılmış hâlidir. Üstelini almadan önce skorlardan en büyüğünü çıkar.

# önceki bloktaki z = (1000, 1001, 1002) ileshifted = z - z.max()shifted                   # → array([-2., -1.,  0.])exps = np.exp(shifted)exps                      # → array([0.13533528, 0.36787944, 1.        ])print(exps.sum())         # → 1.5032147244080551exps / exps.sum()         # → array([0.09003057, 0.24472847, 0.66524096])

En büyüğünü çıkarmanın sebebi üç özelliktir:

  • Bütün üsler ≤0\le 0 olur, yani e⋅≤1e^{\cdot} \le 1 çıkar. Overflow olmaz.
  • En büyük skorun üstel değeri tam 1’dir. Toplam en az 1 olur ve sıfıra bölme olmaz.
  • Çok küçük üstel değerler 0’a yuvarlanabilir. O sınıfın olasılığı zaten ihmal edilebilir.

Sonuç değişmez, ama hesap sayısal olarak kararlı (numerically stable) olur. Buna sayısal kararlılık (numerical stability) denir.

Üç skoru sürükle, sonra “maksimumu çıkar”ı aç ve olasılıklara bak.

softmax_explorer
−10−505z · logitp = exp(z) / Σ exp(z)z₁2,0→0,665z₂1,0→0,245z₃0,0→0,090toplamΣ = 1

exp: e2,0 = 7,389 · e1,0 = 2,718 · e0,0 = 1,000 · toplam 11,107 p = 0,665 · 0,245 · 0,090

Batch için softmax

Bir batch için fonksiyon, bu adımları her satıra ayrı uygular:

def softmax(logits):    shifted = logits - logits.max(axis=1, keepdims=True)    exps = np.exp(shifted)    return exps / exps.sum(axis=1, keepdims=True)Z = np.array([[2.0, 1.0, 0.0],              [1000.0, 1001.0, 1002.0]])softmax(Z)# → array([[0.66524096, 0.24472847, 0.09003057],#          [0.09003057, 0.24472847, 0.66524096]])softmax(Z).sum(axis=1)               # → array([1., 1.])Z.max(axis=1).shape                  # → (2,)Z.max(axis=1, keepdims=True).shape   # → (2, 1)

Dikkat

Bir batch’te softmax’ı her örneğin kendi KK skoruna ayrı uygula. Yani sınıfların olduğu eksen (axis) boyunca, satır başına uygula. Sütun boyunca, yani örnekler arasında uygularsan her sütunun toplamı 1 olur ve bir örneğin olasılıkları anlamını yitirir.

En büyük skoru ve üstel toplamı da her satır için ayrı alırsın. Bunlar (n,) shape’inde birer vektördür. Onları ZZ‘den doğrudan çıkaramazsın.

(n,) hizalanınca (1, n) olur. n ile k eşitse sonuç sessizce yanlış çıkar. Vektörü bir sütuna çevir:

  • reshape(-1, 1) çağır.
  • Ya da max ve sum çağrılarında keepdims=True ver.

İkisi de (n, 1) shape’ini korur.

Kısa soruSoftmax hesaplanmadan önce her satırdan o satırın en büyük skoru çıkarılırsa ne olur?

Cevabı göster

Olasılıklar aynı kalır; yalnızca üstel hesabı taşmaz. Her skordan aynı sayıyı çıkarmak, pay ve paydada aynı çarpanı üretir ve bu çarpan sadeleşir. Olasılıklar değişmez, ama üstel değerler artık 1'i aşmaz.

Çok sınıflı cross-entropy

Cross-entropy, kendinden emin ve yanlış olmanın bedelidir. Lojistik regresyonda ikili çapraz entropi (binary cross-entropy), modelin doğru sınıfa verdiği olasılık küçüldükçe büyüyen bir kayıp fonksiyonuydu (loss function)kayıp fonksiyonu (loss function): Modelin tahminlerinin ne kadar yanlış olduğunu tek bir sayıyla ölçen fonksiyon.. Aynı fikir KK sınıfa taşınır ve adı cross-entropy olarak kalır.

Doğru sınıfın eksi logaritması

Etiketler (label) 0,1,…,K−10, 1, \ldots, K-1 arasında tam sayıdır. PP softmax çıktısı olsun; log⁡\log doğal logaritmadır. ii. örneğin doğru sınıfı yiy_i ise loss, modelin o sınıfa verdiği olasılığın eksi logaritmasıdır:

ℓi=−log⁡Pi, yi,L=1N∑i=1Nℓi\ell_i = -\log P_{i,\,y_i}, \qquad L = \frac{1}{N}\sum_{i=1}^{N} \ell_i

Simgeler şunlardır:

  • Pi, yiP_{i,\,y_i}, PP‘nin ii. satırındaki yiy_i. sütunudur.
  • ℓi\ell_i tek bir örneğin loss’udur.
  • LL batch’in ortalamasıdır.

Doğru sınıfa verilen olasılık 1 ise loss 0’dır. Bu olasılık 0’a giderse loss sonsuza gider.

Yukarıdaki p\mathbf{p} için (yuvarlanmamış olasılıklarla) loss, doğru sınıfa göre değişir:

Doğru sınıfModelin verdiği olasılıkℓ\ell
00,6650,4076
10,2451,4076
20,0902,4076

Modelin en çok güvendiği sınıf doğruysa loss küçük, en az güvendiği sınıf doğruysa büyüktür.

import numpy as npz = np.array([2.0, 1.0, 0.0])probs = np.exp(z) / np.exp(z).sum()-np.log(probs)    # → array([0.40760596, 1.40760596, 2.40760596])

İki örnekli bir batch alalım:

  1. İlk satır (2; 1; 0)(2;\ 1;\ 0), doğru sınıf 0: ℓ1=0,4076\ell_1 = 0{,}4076.
  2. İkinci satır (0; 0; 0)(0;\ 0;\ 0), doğru sınıf 1: model her sınıfa 1/31/3 verir ve ℓ2=log⁡3≈1,0986\ell_2 = \log 3 \approx 1{,}0986 olur.
  3. Ortalamayı al: L=(0,4076+1,0986)/2≈0,7531L = (0{,}4076 + 1{,}0986)/2 \approx 0{,}7531.

NumPy’da probs[np.arange(n), y], her satırdan doğru sınıfın olasılığını seçer:

logits = np.array([[2.0, 1.0, 0.0],                   [0.0, 0.0, 0.0]])y = np.array([0, 1])n = len(y)probs = softmax(logits)                 # önceki bölümdeki softmaxp_true = probs[np.arange(n), y]         # her satırdan doğru sınıfp_true                                  # → array([0.66524096, 0.33333333])losses = -np.log(p_true)losses                                  # → array([0.40760596, 1.09861229])print(losses.mean())                    # → 0.7531091265562451

Her sınıfa 1/K1/K veren, yani hiçbir şey bilmeyen bir modelin loss’u her örnekte log⁡K\log K olur. K=10K = 10 için log⁡10≈2,303\log 10 \approx 2{,}303 çıkar.

Bundan yüksek bir loss, modelin her sınıfa eşit olasılık veren bir modelden bile kötü olduğunu gösterir. log⁡K\log K bir kıyas noktasıdır.

print(np.log(3))     # → 1.0986122886681098print(np.log(10))    # → 2.302585092994046

One-hot yazılışı

Kitaplarda aynı şey one-hot label’larla yazılır. One-hot label, doğru sınıfta 1, ötekilerde 0 olan satırdır.

ℓi=−∑kYiklog⁡Pik\ell_i = -\sum_k Y_{ik}\log P_{ik}

Her satırda tek bir 1 olduğundan toplamda yalnızca doğru sınıfın terimi kalır.

# önceki bloktaki y ve probs ileY = np.eye(3)[y]    # her satır bir one-hot labelY# → array([[1., 0., 0.],#          [0., 1., 0.]])-(Y * np.log(probs)).sum(axis=1)    # → array([0.40760596, 1.09861229])

Logaritmanın tuzağı

Formülü olduğu gibi hesaplamak üç adımdır:

  1. Skorları hesapla.
  2. Softmax al.
  3. Logaritma al.

Son adım tehlikelidir. z=(0; 800; 0)\mathbf{z} = (0;\ 800;\ 0) ve doğru sınıf 0 olsun. En büyüğü çıkarıp softmax alsak bile e−800e^{-800} 0’a yuvarlanır.

Bu durumda P0=0P_{0} = 0 olur ve −log⁡0=∞-\log 0 = \infty çıkar. Oysa gerçek loss sonludur ve 800800‘e yakındır.

Bu, binary cross-entropy’de gördüğün log⁡(0)\log(0) tuzağının aynısıdır. Orada olasılığı ϵ=10−12\epsilon = 10^{-12} ile kırpmıştık. Burada kırpma loss’u −log⁡ϵ≈27,6-\log\epsilon \approx 27{,}6‘da keser, oysa doğru değer 800’dür.

logits = np.array([[0.0, 800.0, 0.0]])probs = softmax(logits)      # Softmax bölümündeki fonksiyonprobs                       # → array([[0., 1., 0.]])print(-np.log(probs[0, 0])) # → infprint(-np.log(1e-12))       # → 27.631021115928547

Logit’lerden doğrudan loss

Çıkış yolu, olasılığı hiç hesaplamamaktır. Logaritma bir bölümü farka çevirir:

log⁡Pi, k=zk−log⁡∑jezj\log P_{i,\,k} = z_k - \log\sum_j e^{z_j}

Böylece tek bir örneğin loss’u doğrudan skorlardan yazılır:

ℓ=−zy+log⁡∑j=0K−1ezj\ell = -z_{y} + \log\sum_{j=0}^{K-1} e^{z_j}

Formül, doğru sınıfın skorunu çıkarır ve bütün skorların üstel toplamının logaritmasını ekler. Doğru sınıfın skoru toplama göre ne kadar geride kalıyorsa loss o kadar büyür.

Örneğin (2; 1; 0)(2;\ 1;\ 0) için log⁡11,107≈2,4076\log 11{,}107 \approx 2{,}4076 olur. Doğru sınıf 0 ise ℓ=−2+2,4076=0,4076\ell = -2 + 2{,}4076 = 0{,}4076 çıkar. Bu, yukarıdakiyle aynıdır.

Tehlikeli kısım, skorların üstel toplamının logaritmasıdır. Orada da aynı yöntemi kullanırız. Toplamı, en büyük skor mm ile şöyle yazarız:

∑jezj=em∑jezj−m\sum_j e^{z_j} = e^{m}\sum_j e^{z_j - m}

Logaritmayı alırsak şunu elde ederiz:

log⁡∑jezj=m+log⁡∑jezj−m,m=max⁡jzj\log\sum_{j} e^{z_j} = m + \log\sum_{j} e^{z_j - m}, \qquad m = \max_j z_j

Logaritmanın içindeki toplamda en büyük terim tam 1’dir. Bu yüzden toplam en az 1’dir. log⁡(0)\log(0) hiç oluşmaz ve üstel değerler taşmaz.

(0; 800; 0)(0;\ 800;\ 0) örneğinde şunu yaparız:

  1. En büyük skoru bul: m=800m = 800.
  2. İçerideki toplamı hesapla: 1+2e−800≈11 + 2e^{-800} \approx 1.
  3. Loss’u yaz: ℓ=−0+800=800\ell = -0 + 800 = 800.

Sonuç sonsuz değil, doğru değerdir.

Aynı yöntem iki yerde işe yarar: en büyük skoru çıkar. İki hesap da koda dökülür:

def log_sum_exp(logits):    m = logits.max(axis=1, keepdims=True)    return m[:, 0] + np.log(np.exp(logits - m).sum(axis=1))def cross_entropy(logits, y):    n = logits.shape[0]    return float(np.mean(log_sum_exp(logits) - logits[np.arange(n), y]))log_sum_exp(np.array([[2.0, 1.0, 0.0]]))                    # → array([2.40760596])cross_entropy(np.array([[2.0, 1.0, 0.0]]), np.array([0]))   # → 0.4076059644443806cross_entropy(np.array([[0.0, 800.0, 0.0]]), np.array([0])) # → 800.0
Kısa soruSınıf sayısı 10 olan bir model, ilk adımda her sınıfa eşit olasılık veriyor. Ortalama cross-entropy loss yaklaşık kaç olur?

Cevabı göster

Yaklaşık 2,3. Doğru sınıfa verilen olasılık 1/10'dur ve loss -log(1/10) = log 10 ≈ 2,303 olur. Bu, hiçbir şey bilmeyen bir modelin loss'udur.

Özet

  • Bir nöron, z=w⊤x+bz = \mathbf{w}^\top \mathbf{x} + b ağırlıklı toplamını bir aktivasyon fonksiyonundan geçirir: a=f(z)a = f(z). ff sigmoid ise ortaya lojistik regresyon çıkar.
  • Aktivasyonsuz layer’lar üst üste konunca sonuç tek bir doğrusal layer’dır. Araya konan doğrusal olmayan bir fonksiyon, ağın eğri fonksiyonları öğrenebilmesini sağlar.
  • Bir dense layer Z=XW+bZ = XW + \mathbf{b} hesaplar (WW: (d_in, d_out)). Aktivasyon ZZ matrisine eleman bazında uygulanır. Dense layer’ları aktivasyonlarla art arda dizersen MLP elde edersin.
  • Sigmoid ve tanh’ın türevi uçlarda sıfıra yaklaşır. Bu küçük türevler layer’lar boyunca çarpılınca gradyan kaybolur ve girişe yakın layer’lar öğrenemez. ReLU’nun türevi pozitif tarafta 1, negatif tarafta 0’dır; hep negatif kalan bir nöron öğrenmeyi bırakır.
  • Softmax KK skoru olasılığa çevirir. Cross-entropy doğru sınıfın olasılığının eksi logaritmasını alır. Üstelini almadan önce en büyük skoru çıkarmak sonucu değiştirmeden taşmayı önler.

Terimler

TürkçeEnglish
nöronneuron
aktivasyon fonksiyonuactivation function
aktivasyon öncesi değerpre-activation
aktivasyon sonrası değeractivation
ağırlıkweight
sapmabias
etiketlabel
evrensel yaklaşım teoremiuniversal approximation theorem
türevderivative
hiperbolik tanjanttanh
gradyangradient
kaybolan gradyanvanishing gradient
kayıploss
geri yayılımbackpropagation
katmanlayer
yoğun katmandense layer
gizli katmanhidden layer
çok katmanlı algılayıcımultilayer perceptron, MLP
örneksample
yığınbatch
matris çarpımımatrix product
şekilshape
yayınlamabroadcasting
eleman bazındaelement-wise
olasılıkprobability
sınıflandırmaclassification
öznitelikfeature
parametreparameter
devriktranspose
ileri geçişforward pass
ham skorlarlogits
eksenaxis
taşmaoverflow
sayısal kararlılıknumerical stability
kayıp fonksiyonuloss function
çapraz entropicross-entropy
ikili çapraz entropibinary cross-entropy
one-hotone-hot
sigmoidsigmoid
ReLUReLU
GELUGELU
softmaxsoftmax
dying ReLUdying ReLU

Şimdi sıra sende

  1. ReLU ve türeviZorluk: KolayBaşla →
  2. Kararlı softmaxZorluk: KolayBaşla →
  3. Yoğun katman ileri geçişiZorluk: OrtaBaşla →
  4. Çok sınıflı çapraz entropiZorluk: ZorBaşla →
İlk problem: ReLU ve türevi →

Notların

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

Geri bildirim

Tür