Sıradaki

Gradyan kontrolü

Zorluk: Orta

kalkülüs

Bir gradyanı (gradient) elle türetirken işareti, bir çarpanı ya da bir ekseni kaçırmak kolaydır. Hatayı yakalamanın güvenilir yolu, elle türetilen analitik gradyanı (analytic gradient) sayısal gradyanla (numerical gradient) karşılaştırmaktır. Buna gradyan kontrolü (gradient check) denir.

gradient_check(f, grad_f, x, h=1e-5) fonksiyonunu yaz. f, bir NumPy dizisi (array) alıp tek bir sayı döndürür.

grad_f, aynı dizi için f’nin elle türetilmiş gradyanını, x ile aynı şekle (shape) sahip bir dizi olarak döndürür. İki gradyanı hesapla:

  • Analitik gradyan ga\mathbf{g}_a, grad_f(x)’tir.
  • Sayısal gradyan gn\mathbf{g}_n, merkezi farklarla (central differences) bulunur.

x’in her elemanı xix_i için sayısal gradyan şudur:

(gn)i=f(x+h ei)−f(x−h ei)2h(\mathbf{g}_n)_i = \frac{f(\mathbf{x} + h\,\mathbf{e}_i) - f(\mathbf{x} - h\,\mathbf{e}_i)}{2h}

ei\mathbf{e}_i, yalnızca ii. elemanı 1, diğerleri 0 olan dizidir. Yalnızca xix_i‘yi hh kadar artırıp azaltırsın. Sonra ikisinin göreli hatasını (relative error) float olarak döndür:

go¨reli hata=∥ga−gn∥2∥ga∥2+∥gn∥2\text{göreli hata} = \frac{\lVert \mathbf{g}_a - \mathbf{g}_n \rVert_2}{\lVert \mathbf{g}_a \rVert_2 + \lVert \mathbf{g}_n \rVert_2}

∥⋅∥2\lVert \cdot \rVert_2, bir dizinin bütün elemanlarının karelerinin toplamının karekökü demektir. x iki boyutluysa da bütün elemanlar tek bir liste gibi sayılır.

Hatanın değeri şöyledir:

  • İki gradyan aynıysa hata 00‘dır.
  • Gradyanlar birbirinden uzaklaştıkça hata artar, en çok 11‘e kadar.
  • İki gradyan da sıfır dizisiyse payda 00 olur. Bu durumda hata 0.0’dır.

Test durumlarında f ve grad_f birer isimle verilir. Testler her ismi fonksiyona, x’i de NumPy dizisine çevirir. Sonra üçünü senin fonksiyonuna verir.

f için isimler şunlardır:

’kareler’:f(x)=∑ixi2’c¸arpım’:f(x)=x0 x1’sinu¨s’:f(x)=∑isin⁡xi’sabit’:f(x)=5\begin{aligned} \texttt{'kareler'}:&\quad f(\mathbf{x}) = \sum_i x_i^2 \\ \texttt{'çarpım'}:&\quad f(\mathbf{x}) = x_0 \, x_1 \\ \texttt{'sinüs'}:&\quad f(\mathbf{x}) = \sum_i \sin x_i \\ \texttt{'sabit'}:&\quad f(\mathbf{x}) = 5 \end{aligned}

grad_f için isimler şunlardır:

İsimGradyan
'2x'2x2\mathbf{x}, 'kareler' için doğru gradyan
'3x'3x3\mathbf{x}, yanlış bir gradyan
'-2x'−2x-2\mathbf{x}, işareti ters bir gradyan
'sıfır'bütün elemanları sıfır olan dizi
'(x1, x0)'(x1,x0)(x_1, x_0), 'çarpım' için doğru gradyan
'cos'cos⁡xi\cos x_i eleman eleman, 'sinüs' için doğru gradyan

Örnekler

Girdi: f = 'kareler', grad_f = '3x', x = [1.0, -2.0, 3.0]
Çıktı: 0.19999999999533288

Girdi: f = 'sabit', grad_f = 'sıfır', x = [1.0, 2.0]
Çıktı: 0.0

Girdi: f = 'sinüs', grad_f = 'cos', x = [0.0, 1.0, -2.0]
Çıktı: 8.40374927161235e-12

İpucu 1

Sayısal gradyanı x’in bir kopyasında hesapla (np.array(x, dtype=float)). Analitik gradyan zaten grad_f(x)’tir.

Her eleman için iki yana bak:

  1. x[i]’yi hh kadar artır ve f’yi hesapla.
  2. x[i]’yi hh kadar azalt ve f’yi hesapla.
  3. Elemanı eski değerine geri koy.

for i in np.ndindex(x.shape): bir ya da iki boyutlu bir dizinin her elemanını dolaşır.

İpucu 2

np.linalg.norm(a), iki boyutlu bir dizide de a’nın bütün elemanlarının karelerinin toplamının karekökünü verir. Bir matriste ord=2 vermek başka bir normu (en büyük tekil değeri) hesaplar. Bu yüzden varsayılanı kullan.

İpucu 3

Payda sıfırsa bölme yapmadan 0.0 döndür. Paydaya küçük bir sayı eklemek formülü değiştirir. Bunun yerine paydanın sıfır olup olmadığını açıkça kontrol et.

Gereksinimler

  • f bir dizi alıp tek bir sayı, grad_f(x) de x ile aynı shape’te bir dizi döndürür. x bir ya da iki boyutlu olabilir.

  • Dönüş değeri tek bir float: array ya da liste değil. np.float64 de kabul edilir. float(...) ile çevirmek zorunda değilsin.

  • İki gradyan da sıfırsa 0.0 döndür. nan ya da hata değil. Paydaya küçük bir sabit ekleme. Formülü olduğu gibi uygula.

  • Sayısal gradyanda merkezi farkı ve verilen h’yi kullan. İleri fark testleri geçecek kadar yakın sonuç vermez. Formülü şudur:

    f(x+h ei)−f(x)h\frac{f(\mathbf{x} + h\,\mathbf{e}_i) - f(\mathbf{x})}{h}
  • x tam sayılardan oluşan bir array de olabilir. Sayısal gradyan yine float hesaplanmalı.

  • x’i değiştirme. Fonksiyon bittiğinde çağıran kodun array’i aynı kalmalı.

Gradyan kontrolü, yanlış türetilmiş bir gradyanı göreli hatayla yakalar. Doğru gradyanı 2x2\mathbf{x} olan şu fonksiyonu alalım:

f(x)=∑ixi2f(\mathbf{x}) = \sum_i x_i^2

Sen yanlışlıkla 3x3\mathbf{x} yazdın. x=(1,−2,3)\mathbf{x} = (1, -2, 3) için hesap şöyledir:

  1. Analitik gradyan: ga=(3,−6,9)\mathbf{g}_a = (3, -6, 9).

  2. Sayısal gradyan: yaklaşık gn=(2,−4,6)\mathbf{g}_n = (2, -4, 6).

  3. Fark: (1,−2,3)(1, -2, 3).

  4. Normlar:

    ∥ga−gn∥2=14,∥ga∥2=314,∥gn∥2=214\lVert \mathbf{g}_a - \mathbf{g}_n \rVert_2 = \sqrt{14}, \qquad \lVert \mathbf{g}_a \rVert_2 = 3\sqrt{14}, \qquad \lVert \mathbf{g}_n \rVert_2 = 2\sqrt{14}
  5. Göreli hata:

    14514=0,2\frac{\sqrt{14}}{5\sqrt{14}} = 0{,}2

Farkın kendisi yerine oranı kullanmak, hatayı gradyanın ölçeğinden bağımsız yapar. Gradyanlar 10410^4 mertebesindeyse 0,010{,}01‘lik bir fark önemsizdir. 10−410^{-4} mertebesindeyse fark hatanın kendisidir.

Kabaca şöyle yorumla:

Göreli hataYorum
10−710^{-7}‘den küçükGradyan doğrudur.
10−710^{-7} ile 10−310^{-3} arasıFonksiyona ve hh‘ye bağlıdır.
10−310^{-3}‘ten büyükGradyan büyük olasılıkla yanlıştır.

Sayısal gradyan yavaştır ve eğitimde kullanılmaz. Her eleman için iki fonksiyon çağrısı gerektirir. Elle yazdığın bir geri geçişi küçük bir girdi üzerinde bir kez kontrol etmek için kullanırsın.

Sayısal gradyanın kendisini Sayısal gradyan probleminde yazabilirsin.

Daha fazlası için Yoğun katmanın geri geçişi ve gradyan kontrolü dersine bak.

Sayısal gradyanı merkezi farklarla hesapla ve göreli hatayı döndür.

  1. Analitik gradyanı al.
  2. Bir kopya üzerinde merkezi farkları hesapla.
  3. İki gradyanın göreli hatasını döndür. Payda sıfırsa 0.0 döndür.
import numpy as npdef gradient_check(f, grad_f, x: np.ndarray, h: float = 1e-5) -> float:    x = np.array(x, dtype=float)    analytic = np.asarray(grad_f(x), dtype=float)    numeric = np.zeros_like(x)    for i in np.ndindex(x.shape):        old = x[i]        x[i] = old + h        f_plus = f(x)        x[i] = old - h        f_minus = f(x)        x[i] = old        numeric[i] = (f_plus - f_minus) / (2 * h)    denominator = np.linalg.norm(analytic) + np.linalg.norm(numeric)    if denominator == 0:        return 0.0    return float(np.linalg.norm(analytic - numeric) / denominator)

Sık yapılan iki hata var:

  • Sıfır paydayı unutmak: iki gradyan da sıfırsa NumPy 0/00/0 için nan verir. Doğru bir gradyan hatalıymış gibi görünür.
  • x tam sayı array’iyse kopyayı float yapmamak: xi+hx_i + h tam sayıya kırpılır ve sayısal gradyan sıfır çıkar.

Bu problemde bir sorun mu var?

solution.py
import numpy as npdef gradient_check(f, grad_f, x: np.ndarray, h: float = 1e-5) -> float:    """Analitik gradyanı sayısal gradyanla karşılaştırıp göreli hatayı döndürür."""    # Kodunu buraya yaz.    return 0.0

İçindekiler

Geri bildirim

Tür