Büyük Veri Analitiğinde Olasılıksal Veri Yapıları: HyperLogLog (HLL) Tasarımı ve Analizi
Cardinality Estimation problemini çözen HyperLogLog algoritmasının harici kütüphane bağımlılığı olmadan sıfırdan Python implementasyonu, teorik hata analizi ve kapsamlı test süiti.
- Proje Hakkında
- Algoritma: HyperLogLog Nedir?
- Mimari ve Bileşenler
- Matematiksel Temel
- Kurulum ve Çalıştırma
- Kullanım Örnekleri
- Test Sonuçları
- Hata Analizi
- Proje Yapısı
- Referanslar
Bu proje, Kardinalite Tahmini (Cardinality Estimation) problemini olasılıksal yöntemlerle çözen HyperLogLog (HLL) algoritmasının:
- Sıfırdan (from scratch) Python implementasyonu —
hyperloglogpaketi veya benzeri hazır kütüphaneler kullanılmamıştır. - Teorik hata analizinin matematiksel olarak doğrulanması.
- Birleştirilebilirlik (mergeability) özelliğinin veri kayıpsız olarak gösterilmesi.
| Yöntem | Bellek | Hata | Ölçeklenebilirlik |
|---|---|---|---|
| Tam sayım (HashSet) | O(n) | %0 | ❌ Milyarlarca elemanda imkansız |
| Linear Counting | O(n/log n) | Düşük | |
| HyperLogLog | O(log log n) | ~%1–3 | ✅ Milyarlarda çalışır |
Redis, Google BigQuery, Amazon Redshift ve Elasticsearch gibi üretim sistemleri HLL kullanır.
HyperLogLog, Philippe Flajolet ve arkadaşları (2007) tarafından geliştirilen, olasılıksal bit gözlemi temelli bir kardinalite tahmincisidir.
Bir kümedeki benzersiz eleman sayısını bulmak için her elemanı hash'leyip hash değerinin başındaki sıfır dizisinin uzunluğuna bakılır. İstatistiksel olarak:
- Eğer maksimum gözlemlenen baştaki sıfır sayısı
kise, kardinalite tahminimiz2^k'dır. - Birden fazla kova (bucket) kullanarak varyans düşürülür.
- Kovalar üzerinde harmonik ortalama alınarak sapma azaltılır.
Eleman gelir → Hash'le (MurmurHash3 / SHA-256)
↓
[ b1 b2 ... bp | bp+1 ... b32 ]
↑ ↑
İlk p bit: Kalan (32-p) bit:
Kova indeksi j ρ = baştaki sıfır sayısı + 1
↓
registers[j] = max(registers[j], ρ)
↓
E = α_m · m² · (Σ 2^{-M[j]})⁻¹
↓
Düzeltme faktörleri (küçük/büyük aralık)
↓
Kardinalite tahmini
class MurmurHash3:
"""Austin Appleby'ın MurmurHash3 algoritması, sıfırdan bit manipülasyonu."""Neden MurmurHash3?
- Kriptografik hash'lere (SHA-256) kıyasla 10–20x daha hızlı
- Düşük çarpışma oranı ve uniform dağılım
- HLL için ideal: hız kritik, güvenlik değil
- 32-bit çıktı → p ≤ 16 için yeterli bit uzayı
def _get_bucket_and_rho(self, hash_val: int):
j = hash_val >> (self.b - self.p) # İlk p bit → kova indeksi
w = hash_val & ((1 << remaining) - 1) # Kalan bitler
rho = remaining - w.bit_length() + 1 # Baştaki sıfır sayısı + 1m = 2^pkova,p ∈ [4, 16]hassasiyet parametresi- Her kova bağımsız maksimum
ρdeğerini tutar
self.registers: List[int] = [0] * self.m
# Güncelleme: yalnızca yeni ρ daha büyükse yaz
if rho > self.registers[j]:
self.registers[j] = rho- Boyut:
madet integer (pratikte 5-6 bit yeterli) p=10için yalnızca 640 byte bellek
harmonic_sum = sum(2.0 ** (-r) for r in self.registers)
E = self._alpha * (self.m ** 2) / harmonic_sumNeden Harmonik Ortalama?
Aritmetik ortalama büyük ρ değerlerine aşırı duyarlı (sağa çarpık dağılım). Harmonik ortalama bu outlier etkisini bastırır.
# α_m sabiti (Flajolet et al., 2007)
_ALPHA = {16: 0.673, 32: 0.697, 64: 0.709}
_ALPHA_DEFAULT_LARGE = 0.7213 # m ≥ 128 için
# Küçük aralık: Linear Counting
if E <= 2.5 * self.m and zero_count > 0:
E = self.m * math.log(self.m / zero_count)
# Büyük aralık düzeltmesi
if E > 2^b / 30:
E = -2^b * log(1 - E / 2^b)| Aralık | Koşul | Yöntem |
|---|---|---|
| Küçük | E < 2.5·m ve sıfır register var | Linear Counting |
| Orta | 2.5·m ≤ E ≤ 2^b/30 | Ham HLL tahmini |
| Büyük | E > 2^b/30 | Log-tabanlı düzeltme |
def merge(self, other: "HyperLogLog") -> "HyperLogLog":
merged.registers = [max(a, b) for a, b in zip(self.registers, other.registers)]Özellik: merge(HLL_A, HLL_B) → |A ∪ B| tahmini, orijinal veriye erişim gerektirmez.
Bu özellik dağıtık sistemlerde (farklı sunuculardan gelen HLL'lerin merkezi toplanması) kritik öneme sahiptir.
| p | m = 2^p | σ (Teorik) | Bellek |
|---|---|---|---|
| 4 | 16 | %26.00 | 10 B |
| 8 | 256 | %6.50 | 160 B |
| 10 | 1,024 | %3.25 | 640 B |
| 12 | 4,096 | %1.63 | 2.5 KB |
| 14 | 16,384 | %0.81 | 10 KB |
| 16 | 65,536 | %0.41 | 40 KB |
Sonuç: m her 4 katına çıktığında hata yarıya düşer — O(1/√m) karmaşıklığı.
Python >= 3.8
matplotlib # yalnızca grafik üretimi içingit clone https://github.com/KULLANICI_ADI/hyperloglog-hll.git
cd hyperloglog-hll
pip install matplotlib # opsiyonel, analiz grafikleri içinpython tests/test_hyperloglog.pypython analysis/analysis.py
# Çıktı: docs/hll_analysis.pngfrom src.hyperloglog import HyperLogLog
# p=10 → m=1024 kova, teorik hata ≈ %3.25
hll = HyperLogLog(p=10)
# Eleman ekleme
for user_id in range(1_000_000):
hll.add(f"user_{user_id}")
print(f"Tahmin: {hll.count():,.0f}") # ≈ 1,000,000
print(f"Teorik hata: ±{hll.error_bound()*100:.2f}%")
print(f"Bellek: {hll.memory_bytes()} byte")hll_server_1 = HyperLogLog(p=10)
hll_server_2 = HyperLogLog(p=10)
# Her sunucu kendi verisini işler
hll_server_1.add_batch([f"event_{i}" for i in range(50_000)])
hll_server_2.add_batch([f"event_{i}" for i in range(30_000, 80_000)])
# Orijinal veriye gerek kalmadan birleştir
hll_union = hll_server_1.merge(hll_server_2)
print(f"Toplam benzersiz event tahmini: {hll_union.count():,.0f}")
# Gerçek: 80,000 → Tahmin: ~78,000–82,000# Daha geniş bit uzayı gerekiyorsa (büyük kardinaliteler)
hll = HyperLogLog(p=12, use_sha256=True)TEST 1: Hash Fonksiyonu Kalitesi ✓ Uniform dağılım doğrulandı
TEST 2: Kardinalite Doğruluğu ✓ 100 → 1,000,000 arası tüm PASS
TEST 3: m vs Hata Analizi ✓ p=4..16, monoton azalma doğrulandı
TEST 4: Merge Özelliği ✓ 5 senaryo, tüm PASS
TEST 5: Belirleyicilik (Determinism) ✓ 5 çalıştırma = özdeş sonuç
TEST 6: Teorik Formül Doğrulaması ✓ σ ≈ 1.04/√m tablo üretildi
TEST 7: Farklı Veri Tipleri ✓ int, float, str, UUID — tüm PASS
Grafikler şunları göstermektedir:
- Teorik vs Ampirik hata — formülün ampirik olarak doğrulanması
- Kardinalite sweep — LC ↔ HLL ↔ Büyük aralık geçiş noktaları
- Bellek-Doğruluk takas eğrisi — log-log ölçekte doğrusal ilişki
- σ = 1.04/√m formülü — p değerlerine göre görselleştirme
hyperloglog/
├── src/
│ └── hyperloglog.py # Çekirdek HLL implementasyonu
│ ├── MurmurHash3 # 32-bit hash fonksiyonu
│ ├── sha256_hash() # 64-bit alternatif hash
│ └── HyperLogLog # Ana sınıf
├── tests/
│ └── test_hyperloglog.py # 7 test kategorisi
├── analysis/
│ └── analysis.py # Hata analizi + matplotlib grafikleri
├── docs/
│ └── hll_analysis.png # Üretilen analiz grafikleri
└── README.md
- Flajolet, P., Fusy, É., Gandouet, O., & Meunier, F. (2007). HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm. DMTCS Proceedings, AH, 137–156.
- Heule, S., Nunkesser, M., & Hall, A. (2013). HyperLogLog in Practice: Algorithmic Engineering of a State of The Art Cardinality Estimation Algorithm. EDBT/ICDT.
- Appleby, A. MurmurHash3. https://github.com/aappleby/smhasher
MIT License — Detaylar için LICENSE dosyasına bakınız.
