İçeriğe geç

Normal Dağılım, Z-Score, Modified Z-Score ve MAD: Anomaly Detection'ın İstatistiksel Aleti

Normal dağılımın anomaly detection için anlamı; z-score formülü, sezgisi ve sınırları; modified z-score ve MAD (Median Absolute Deviation) — outlier'a dirençli alternatifler; from-scratch Python implementasyon.

Şükrü Yusuf KAYA
30 dakikalık okuma
Başlangıç
Normal Dağılım, Z-Score, Modified Z-Score ve MAD: Anomaly Detection'ın İstatistiksel Aleti
📐 Matematik korkma — sezgi önce
Bu modülde formüller var. Ama formülle başlamayacağız. Önce sezgi, sonra görsel, en son denklem. Bu yolu izlediğinde z-score gibi 100 yaşındaki bir aracın bugün hâlâ neden production'da yaşadığını kavrarsın. Lab bölümünde de hepsini Python'dan sıfırdan yazacağız — kütüphane değil. Anlamak için bir kez kendi elinle yapmalısın.

Normal Dağılım Neden Bu Kadar Önemli?#

İstatistik literatürünün belkemiği: normal dağılım (Gauss, bell curve). Birçok doğal olay normal dağılır — ama hepsi değil. Anomaly detection'da normal dağılımı bilmemiz iki nedenle:
  1. Pek çok istatistiksel test normal varsayar. Z-score, t-test, Grubbs — hepsinin altında "veri normal dağılıyor" varsayımı var. Bu varsayım bozulursa yanlış cevap alırsın.
  2. "Normal" olduğunu varsaydığımız davranış pratikte sıklıkla normal dağılmaz. Örneğin kart işlem tutarları log-normal, web request süresi Pareto-tail, sensör okumaları multi-modal. Doğru dağılımı tanımak, doğru aracı seçmektir.

Normal Dağılımın Yoğunluk Fonksiyonu#

Bir tek-değişkenli normal dağılımın olasılık yoğunluk fonksiyonu (PDF):
Burada:
  • μ (mu) — dağılımın merkezi (ortalama)
  • σ (sigma) — dağılımın yayılımı (standart sapma)
  • Eğri simetrik, çan şeklinde
  • Toplam alan = 1 (olasılık aksiyomu)

68-95-99.7 Kuralı (Empirical Rule)#

Normal bir dağılımda:
  • %68 veri μ ± 1σ aralığında
  • %95 veri μ ± 2σ aralığında
  • %99.7 veri μ ± 3σ aralığında
Bu kural, z-score ile anomaly detection'ın altyapısıdır: 3σ dışında kalan bir gözlemi anomali olarak işaretlersek, normal dağılan veride sadece %0.3 false positive yaparız. 4σ → %0.006, 5σ → %0.00006.

Z-Score: Standart Sapma Cinsinden Mesafe#

Bir gözlemin kaç sigma uzakta olduğunu söyleyen ölçü:
Sezgi: z-score "bu gözlem dağılımın merkezinden kaç sigma uzakta?" sorusunun cevabı.
| |z| değeri | Yorum | Empirical olasılık (normal'de) | |---|---|---|---| | 0 | Merkezde | – | | 1 | Tipik | %68 içinde | | 2 | Az aykırı | %95 içinde | | 3 | Outlier şüphesi | %99.7 içinde | | 4 | Güçlü outlier | %99.994 içinde | | 5 | Aşırı outlier | %99.99994 içinde | | 6+ | Pratikte imkansız (normal'de) | – |
Pratik kural: |z| > 3 outlier eşiği geleneksel kabul. Ama dağılım normal değilse bu kural yanıltıcı olabilir.
python
import numpy as np
 
def z_score(x, axis=None):
"""
Klasik z-score: (x - mean) / std
"""
mu = np.mean(x, axis=axis, keepdims=True)
sigma = np.std(x, axis=axis, ddof=0, keepdims=True)
return (x - mu) / (sigma + 1e-9) # küçük epsilon ile sıfıra bölmeyi önle
 
# Kullanım
np.random.seed(42)
data = np.random.normal(loc=100, scale=15, size=1000)
data = np.append(data, [200, 250, 300]) # 3 outlier ekle
 
z = z_score(data)
outliers = np.where(np.abs(z) > 3)[0]
print(f"Outlier sayısı (|z|>3): {len(outliers)}")
print(f"Outlier değerler: {data[outliers]}")
Z-score implementasyonu — from scratch

Z-Score'un Sinsi Zayıflığı: Masking#

Z-score basit ve hızlı, ama bir sorunu var: kendi outlier'larına karşı duyarlı. Mean ve std outlier'lardan etkilenir. Sonuç: birkaç büyük outlier, kendi z-score'larını küçük gösterir ve diğer gerçek outlier'ları gizler. Bu fenomene literatürde "masking" denir.

Somut Örnek#

Diyelim ki 100 normal değer (μ=100, σ=10) + 5 outlier (değer=500) var:
data = np.concatenate([np.random.normal(100, 10, 100), [500]*5]) mu = data.mean() # ≈ 119 ← outlier'lar mean'i yukarı çekti sigma = data.std() # ≈ 78 ← outlier'lar std'yi büyüttü # Bir outlier'ın z-score'u: z_outlier = (500 - 119) / 78 # ≈ 4.88 # 4.88 büyük ama 3 sigma kuralıyla yine de yakalanır # Şimdi normal bir 130 değerini düşün: z_normal = (130 - 119) / 78 # ≈ 0.14 # 0.14 — değer normalmiş gibi görünüyor
Masking sorunu büyük outlier kümeleri olduğunda gerçek outlier'ları saklar. Bu, anomaly detection'da kritik problem — özellikle DDoS attack gibi senaryolarda yüz binlerce outlier birlikte gelir.
🛡️ Robust istatistik ihtiyacı
Z-score'un kendi outlier'larına karşı kırılganlığı, robust istatistik alanını doğurdu: outlier'lara karşı dirençli istatistikler kullanmak. Bunun en yaygın aracı: median + MAD.

MAD: Median Absolute Deviation#

MAD, "median'dan medyan mutlak sapma" anlamına gelir. Outlier'a karşı çok daha dirençlidir:

Neden Robust?#

Hatırla: medyan, dizinin ortasındaki değerdir. Bir outlier'ın medyana etkisi minimal. Bir outlier'ın mean'e etkisi büyük.
Breakdown point:
  • Mean: %0 — bir tek outlier mean'i istediğin kadar bozar
  • Median: %50 — verinin yarısı outlier olsa bile median değişmez
MAD aynı %50 breakdown point'i taşır. Bu, "verinin %49'u kötü olsa bile MAD hâlâ çalışır" demek.

Modified Z-Score (MAD-based)#

Klasik z-score'un MAD versiyonu:
Buradaki 0.6745 sihirli sayı değil — normal dağılım altında MAD'in standart sapmaya yaklaşması için gerekli ölçeklendirme. (Iglewicz ve Hoaglin, 1993)

Modified Z-Score Eşiği#

| |M| değeri | Yorum | |---|---| | 0 | Median'da | | 2 | Az aykırı | | 3.5 | Outlier şüphesi (geleneksel eşik) | | 5+ | Güçlü outlier |
Iglewicz-Hoaglin önerisi: |M| > 3.5 outlier eşiği. Klasik z-score'daki 3'ten neden farklı? Çünkü MAD daha "konservatif" ölçek; aynı outlier rate'i tutmak için biraz daha yüksek eşik gerek.
python
def mad(x):
"""Median Absolute Deviation"""
med = np.median(x)
return np.median(np.abs(x - med))
 
def modified_z_score(x):
"""Modified z-score with MAD"""
med = np.median(x)
m = mad(x)
if m == 0:
# Tüm değerler aynıysa — MAD = 0 — fallback'e dön
return np.zeros_like(x, dtype=float)
return 0.6745 * (x - med) / m
 
# Yukarıdaki masking örneğini tekrar dene
data = np.concatenate([np.random.normal(100, 10, 100), [500]*5])
 
# Klasik z-score
z = z_score(data)
print(f"Klasik z-score outlier sayısı (|z|>3): {(np.abs(z) > 3).sum()}")
 
# Modified z-score (MAD-based)
m = modified_z_score(data)
print(f"Modified z-score outlier sayısı (|M|>3.5): {(np.abs(m) > 3.5).sum()}")
MAD + Modified Z-Score implementasyonu

Z-Score vs Modified Z-Score: Yan Yana Karşılaştırma#

ÖzellikKlasik Z-ScoreModified Z-Score (MAD)
Merkez ölçüsüMeanMedian
Yayılım ölçüsüStd sapmaMAD
Breakdown point%0%50
HesaplamaHızlıOrta
Outlier dirençiDüşükYüksek
Normal varsayımıGüçlüHafif
Eşikz
Production tercihiÇok azaYaygın

Hangisini Ne Zaman?#

  • Klasik z-score: Veri kesinlikle (veya neredeyse kesinlikle) normal dağılan; outlier oranı çok düşük (<%1); hız kritik.
  • Modified z-score: Veri kirli olabilir; outlier oranı orta-yüksek (%5+); robust gerekiyor.
  • Pratik öneri: Üretimde başlangıç olarak modified z-score; klasik z-score'a yalnızca normal-varsayan bir test (Grubbs) çıktısını besliyorsan dön.

Pratik İpucu: Log-Normal Veri için Önce Log Al#

Çoğu finansal veri log-normal dağılır (kart işlem tutarı, gelir, gecikme süreleri). Doğrudan z-score yanlış cevap verir. Önce
log
veya
log1p
uygula, sonra z-score:
# Yanlış yaklaşım amounts = [100, 150, 200, ..., 50000, 80000] # tutarlar log-normal z = z_score(amounts) # Büyük tutarlar abartılı z-score alır # Doğru yaklaşım log_amounts = np.log1p(amounts) z = z_score(log_amounts) # Daha düzgün dağılım, sağlıklı z-score
Modül 3'te (Veri Hazırlığı) bu dönüşümleri sistematik ele alacağız.
💼 Production Pearl
Türk bir bankada fraud feature pipeline'ı 2023'te z-score'lardan modified z-score'lara geçti. Sadece bu değişiklikle fraud detection PR-AUC'ı %4 arttı, alarm false positive %12 düştü. Bir kelime: 'mean'i median' yap. Sermaye getirisi: aylık ~600K TL analist saati.

Bu Yöntemlerin Sınırları#

Z-score ve MAD güçlü ama her şey değil. Açık zayıflıkları var:

1. Tek-değişkenli#

Z-score tek özellik üzerinde çalışır. Çok-değişkenli anomalileri yakalayamaz. (Örn: müşterinin kombinasyonu "yüksek tutar + yeni alıcı + gece saati" anomali; tek tek hepsi normal.) Bunun için Mahalanobis distance (Modül 8) lazım.

2. Statik#

Anlık dağılımdan z-score hesaplıyorsun. Zamana göre değişen dağılım (concept drift) yakalanmaz. Rolling z-score gerekir.

3. Dağılımı varsayar#

"Z-score 3" eşiği normal dağılımda %0.3 yapar. Pareto-tail dağılımda çok daha fazla false positive üretir. EVT/POT (Modül 2.4) bunun çözümü.

4. Bağlamı bilmez#

Z-score "müşterinin profili" gibi bağlamı modele entegre etmez. Bu, Modül 1.2'deki contextual anomaly tipinin sınırı.
Bu sınırların hepsini sonraki modüllerde aşacağız. Şu an için z-score + MAD ikilisi temel taşı — tüm sonraki yöntemler bu üstüne bina ediliyor.
👉 Bir sonraki ders
Ders 2.2 — IQR, Tukey's Fences, Adjusted Boxplot. Çeyrekler ve quartile-based outlier tespiti. Z-score'un kuzenleri ama daha az dağılım varsayımıyla. Skewed (asimetrik) veride z-score'dan üstün.

Sık Sorulan Sorular

Tarihsel bir konvansiyon — Shewhart (1931) kalite kontrol kartları için 3σ önerdi. Empirically, normal dağılımda %0.3 false positive verir. Pratikte sektöre göre 2 (daha duyarlı) veya 4 (daha konservatif) seçilebilir. Modern AD sistemlerinde POT veya percentile tabanlı dinamik eşik tercih ediliyor.

Yorumlar & Soru-Cevap

(0)
Yorum yazmak için giriş yap.
Yorumlar yükleniyor...

İlgili İçerikler