Vektörleştirme: döngüyü bırakmak
Önkoşul:Broadcasting
Kanca
1 milyon sipariş tutarına %20 KDV eklemek gerekiyor. Python döngüsüyle 0.237 saniye sürdü. tutarlar * 1.20 yazarak NumPy’a bıraktığında ise 0.00173 saniye — 137 kat daha hızlı, aynı sonuç. Bu fark neden bu kadar büyük, ve her zaman böyle mi kalıyor?
Sezgi
- derste NumPy dizilerinin “vektörleştirilmiş düşünce” ile çalıştığını görmüştük — işlemi tek tek elemanlara değil, tüm diziye tarif ediyorsun. Bu derste bunun sadece bir kolaylık değil, gerçek bir performans farkı olduğunu göreceğiz.
Python döngüsü, her adımda “bu bir sayı mı, ne tip, nasıl çarpılır” gibi kontroller tekrar tekrar yapar — NumPy vektörleştirmesi ise bu kontrolü bir kez yapıp, geri kalanını derlenmiş, optimize edilmiş C kodunda yürütür. Küçük dizilerde bu fark önemsizdir, ama veri büyüdükçe döngünün maliyeti doğrusal artarken vektörleştirme neredeyse sabit kalır.
Mekanizma
Somut ölçümlerle ilerleyelim. 1 milyon elemanlık bir diziye KDV ekleyelim:
1 milyon elemanlık veri
# 1 milyon sipariş tutarına %20 KDV ekleyeceğiz — gerçekçi bir "büyük veri" boyutu.
n = 1_000_000
tutarlar = rng.uniform(10, 500, size=n)
print(f"{n:,} elemanlı dizi hazır.")1,000,000 elemanlı dizi hazır.Python döngüsüyle
baslangic = time.perf_counter()
kdvli_loop = np.empty(n)
for i in range(n):
kdvli_loop[i] = tutarlar[i] * 1.20
sure_loop = time.perf_counter() - baslangic
print(f"Python döngüsü: {sure_loop:.3f} saniye")Python döngüsü: 0.237 saniyeNumPy vektörüyle
baslangic = time.perf_counter()
kdvli_vektor = tutarlar * 1.20
sure_vektor = time.perf_counter() - baslangic
print(f"NumPy vektörleştirme: {sure_vektor:.5f} saniye")
print(f"\nSonuçlar aynı mı? {np.allclose(kdvli_loop, kdvli_vektor)}")
print(f"Hızlanma: {sure_loop / sure_vektor:.0f}x")NumPy vektörleştirme: 0.00173 saniye
Sonuçlar aynı mı? True
Hızlanma: 137x0.237 saniyeye karşı 0.00173 saniye — 137 kat fark, ve sonuçlar birebir aynı. Çoğu kişi “döngü de sonuçta aynı işi yapıyor, fark küçük olmalı” sanır. Değil, çünkü Python’un her döngü adımı, tipini önceden bilmediği bir nesneyle uğraştığı için sürekli “bu ne tip bir şey, nasıl işlem yapılır” diye kontrol eder — NumPy bu kontrolü tamamen ortadan kaldırıp veriyi doğrudan, tek tip bir bellek bloğu olarak işler.
Matematik
Neden bu kadar hızlı?
Kesin bir formül yok, ama sezgi şu iki gözlemden geliyor:
| Yaklaşım | Her adımda olan | Toplam maliyet |
|---|---|---|
| Python döngüsü | Tip kontrolü + nesne oluşturma + işlem | n × (sabit ek yük + işlem) |
| NumPy vektör | Tip kontrolü (1 kez, en başta) + derlenmiş C döngüsü | n × (sadece işlem) + küçük sabit |
n küçükken iki yaklaşımın de “sabit ek yük” kısmı baskındır, fark azdır. n büyüdükçe Python döngüsünün her adımdaki ek yükü toplamda katlanarak büyür, NumPy’ın tek seferlik kurulum maliyeti ise görece küçük kalır — bu yüzden fark n arttıkça açılır.
Kod
n’in büyüklüğünün hızlanma oranını nasıl etkilediğini görelim:
n arttıkça hızlanma
# n büyüdükçe fark nasıl açılıyor? Birkaç farklı boyutta ölçelim.
print("\nn arttıkça hızlanma oranı:")
for n_deneme in [1_000, 10_000, 100_000, 1_000_000]:
veri = rng.uniform(10, 500, size=n_deneme)
t0 = time.perf_counter()
sonuc_loop = np.empty(n_deneme)
for i in range(n_deneme):
sonuc_loop[i] = veri[i] * 1.20
t_loop = time.perf_counter() - t0
t0 = time.perf_counter()
sonuc_vektor = veri * 1.20
t_vektor = time.perf_counter() - t0
print(f" n={n_deneme:>9,} döngü={t_loop:.4f}s vektör={t_vektor:.5f}s hızlanma={t_loop / t_vektor:.0f}x")
n arttıkça hızlanma oranı:
n= 1,000 döngü=0.0003s vektör=0.00001s hızlanma=48x
n= 10,000 döngü=0.0025s vektör=0.00004s hızlanma=69x
n= 100,000 döngü=0.0248s vektör=0.00017s hızlanma=149x
n=1,000,000 döngü=0.2490s vektör=0.00181s hızlanma=138xn=1.000’de hızlanma 48x, n=100.000’de 149x’e çıkıyor — küçük veride bile vektörleştirme kazandırıyor, büyük veride kazanç çok daha belirgin hale geliyor.
Nerede işe yarar
Vektörleştirme alışkanlığı, veri biliminde performansın (ve bazen “çalışır mı çalışmaz mı”nın) belirleyicisidir:
- Büyük veri setleri. Milyonlarca satırlık bir müşteri veya işlem tablosunda döngü kullanmak, işlemi dakikalara, hatta saatlere uzatabilir.
- Model eğitimi. Makine öğrenmesi kütüphaneleri (scikit-learn, PyTorch) neredeyse tamamen vektörleştirilmiş işlemler üzerine kuruludur.
- Üretim sistemleri. Bir API’nin her istekte binlerce hesaplama yapması gerekiyorsa, vektörleştirme yanıt süresini doğrudan etkiler.
Bu 3 hatayı yaparsın:
- Küçük veride “fark etmez” diye döngü alışkanlığını sürdürmek — veri büyüdüğünde bu alışkanlık pahalıya patlar.
- Vektörleştirilebilir bir işlemi
fordöngüsü +ifbloklarıyla yazıp sonra “NumPy yavaş” diye şikayet etmek — yavaş olan döngü, NumPy değil. - Her durumda vektörleştirmenin mümkün olduğunu sanmak — bazı sıralı/durum bağımlı hesaplamalar (bir öncekine bağlı olanlar) doğrudan vektörleştirilemez, özel teknikler gerekir.
Kendini test et
1. Python döngüsü ile NumPy vektörleştirmesi arasındaki hız farkının temel kaynağı nedir?
- NumPy farklı bir programlama dilinde çalışır
- Döngü her adımda tekrar tip kontrolü yapar, vektörleştirme bunu bir kez yapıp derlenmiş kod çalıştırır (doğru cevap)
- NumPy daha az doğru sonuç verir ama daha hızlıdır
- Aralarında gerçek bir fark yoktur
Neden: Python'un dinamik tip kontrolü her döngü adımında tekrarlanan bir maliyettir; NumPy bu kontrolü bir kez yapıp geri kalanını optimize edilmiş, derlenmiş kodla yürütür.
2. n=1.000'de hızlanma 48x iken n=1.000.000'da 138x çıkması neyi gösterir?
- Ölçüm hatası olduğunu
- Vektörleştirmenin faydası, veri büyüdükçe artma eğiliminde olduğunu (doğru cevap)
- Küçük veride vektörleştirmenin işe yaramadığını
- NumPy'ın büyük veride yavaşladığını
Neden: Döngünün sabit ek yükü toplamda n ile doğrusal büyürken, vektörleştirmenin tek seferlik kurulum maliyeti görece sabit kalır — bu yüzden fark büyük n'de daha belirgin hale gelir.
3. Vektörleştirme her zaman mümkün müdür?
- Evet, her hesaplama vektörleştirilebilir
- Hayır — bir adımın bir öncekine bağlı olduğu sıralı hesaplamalar doğrudan vektörleştirilemeyebilir (doğru cevap)
- Sadece sayısal olmayan verilerde mümkündür
- Sadece küçük dizilerde mümkündür
Neden: Bağımsız elemanlar üzerinde çalışan işlemler kolayca vektörleştirilir; ama her adımın bir öncekinin sonucuna ihtiyaç duyduğu hesaplamalar (bazı zaman serisi işlemleri gibi) özel yaklaşımlar gerektirir.
Özet
Özet
- Vektörleştirme, bir işlemi Python döngüsü yerine NumPy'ın derlenmiş, toplu işlem mekanizmasına bırakmaktır.
- Hız farkının kaynağı: döngü her adımda tip kontrolü tekrarlar, vektörleştirme bunu bir kez yapar.
- Fark küçük veride bile belirgindir (örnekte 48x), büyük veride çok daha belirgin hale gelir (138x).
- Vektörleştirilebilir bir işlemi döngüyle yazmak, veri büyüdükçe ciddi performans kaybına yol açar.
- Bazı sıralı/durum bağımlı hesaplamalar doğrudan vektörleştirilemez — bu bir istisna, kural değil.