Gradyan kontrolü
Zorluk: Orta Çözüldü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 ,
grad_f(x)’tir. - Sayısal gradyan , merkezi farklarla (central differences) bulunur.
x’in her elemanı için sayısal gradyan şudur:
, yalnızca . elemanı 1, diğerleri 0 olan dizidir. Yalnızca ‘yi kadar artırıp azaltırsın. Sonra ikisinin göreli hatasını (relative error) float olarak döndür:
, 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 ‘dır.
- Gradyanlar birbirinden uzaklaştıkça hata artar, en çok ‘e kadar.
- İki gradyan da sıfır dizisiyse payda 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:
grad_f için isimler şunlardır:
| İsim | Gradyan |
|---|---|
'2x' | , 'kareler' için doğru gradyan |
'3x' | , yanlış bir gradyan |
'-2x' | , işareti ters bir gradyan |
'sıfır' | bütün elemanları sıfır olan dizi |
'(x1, x0)' | , 'çarpım' için doğru gradyan |
'cos' | 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:
x[i]’yi kadar artır vef’yi hesapla.x[i]’yi kadar azalt vef’yi hesapla.- 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
-
fbir dizi alıp tek bir sayı,grad_f(x)dexile aynı shape’te bir dizi döndürür.xbir ya da iki boyutlu olabilir. -
Dönüş değeri tek bir
float: array ya da liste değil.np.float64de kabul edilir.float(...)ile çevirmek zorunda değilsin. -
İki gradyan da sıfırsa
0.0döndür.nanya 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: -
xtam 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ı olan şu fonksiyonu alalım:
Sen yanlışlıkla yazdın. için hesap şöyledir:
-
Analitik gradyan: .
-
Sayısal gradyan: yaklaşık .
-
Fark: .
-
Normlar:
-
Göreli hata:
Farkın kendisi yerine oranı kullanmak, hatayı gradyanın ölçeğinden bağımsız yapar. Gradyanlar mertebesindeyse ‘lik bir fark önemsizdir. mertebesindeyse fark hatanın kendisidir.
Kabaca şöyle yorumla:
| Göreli hata | Yorum |
|---|---|
| ‘den küçük | Gradyan doğrudur. |
| ile arası | Fonksiyona ve ‘ye bağlıdır. |
| ‘ten büyük | Gradyan 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.
- Analitik gradyanı al.
- Bir kopya üzerinde merkezi farkları hesapla.
- İki gradyanın göreli hatasını döndür. Payda sıfırsa
0.0dö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 için
nanverir. Doğru bir gradyan hatalıymış gibi görünür. xtam sayı array’iyse kopyayıfloatyapmamak: tam sayıya kırpılır ve sayısal gradyan sıfır çıkar.
Bu problemde bir sorun mu var?