İçeriğe geç
Educora
Üniversite25 dk30 / 42

NumPy: vektörleştirme ve doğrusal cebir

Döngüleri dizi ifadeleriyle değiştir, broadcasting kurallarını öğren, dizilerin şeklini değiştir ve `np.linalg` ile denklem sistemlerini çöz, determinant ve özdeğerleri bul.

Kendini test et
Bu derste öğreneceklerin
  • Döngülü kodu vektörleştirilmiş NumPy ifadeleriyle yeniden yazmak ve hız farkını açıklamak
  • Broadcasting kurallarıyla iki dizi arasındaki işlemin sonuç şeklini önceden belirlemek
  • @, np.linalg.solve, det, inv ve eigh fonksiyonlarını matematiksel anlamlarıyla birlikte uygulamak

Murad bir çevrim içi mağazanın bir milyon satışını işliyor: her satış için tutarı hesaplamalı, indirim uygulamalı ve günlük değişimi bulmalı. for döngüsüyle bu saniyeler sürer, NumPy ile milisaniyeler. Sır vektörleştirmededir: işlemi her eleman için ayrı ayrı değil, dizinin tamamı için bir kez yazarız. Bu derste veri biliminin matematiksel dili olan doğrusal cebirle de çalışacağız.

Vektörleştirme: döngüsüz hesaplama

Tanım
Vektörleştirme

Elemanlar üzerindeki Python döngüsünü dizilerin tamamına uygulanan işlemlerle değiştirmek. Döngü yine vardır ama NumPy'ın içinde, derlenmiş C kodunda çalışır. Bu şekilde eleman eleman çalışan fonksiyonlara ufunc (evrensel fonksiyon) denir: +, *, np.sqrt, np.exp, np.where vb.

Python döngüsü neden yavaştır? Her tekrarda yorumlayıcı bir komut okur, elemanın türünü kontrol eder ve sonuç için yeni bir float nesnesi oluşturur. NumPy ise bellekte art arda duran aynı türden sayılar için türü bir kez kontrol eder ve işlemcinin vektör (SIMD) komutlarını kullanır. Kodu çalıştır ve farkı kendi gözünle gör:

Python
import time
import numpy as np

x = np.random.default_rng(0).random(1_000_000)

t0 = time.perf_counter()
total = 0.0
for v in x:
    total += v * v
t_loop = time.perf_counter() - t0

t0 = time.perf_counter()
total_vec = np.dot(x, x)
t_vec = time.perf_counter() - t0

print(f'loop:       {t_loop:.4f} s')
print(f'vectorized: {t_vec:.4f} s')
print('same result:', bool(np.isclose(total, total_vec)))
print(f'speed-up: about {t_loop / t_vec:.0f} times')
Süreler bilgisayara ve tarayıcıya bağlıdır; bu yüzden burada hazır bir çıktı verilmedi. Hızlanma genellikle onlarca, hatta yüzlerce kattır.
DöngüyleVektörleştirilmiş
toplamı biriktiren bir değişkena.sum(), a @ b
her eleman için if / elsenp.where(cond, x, y)
koşula uyan elemanları saymak(a > 0).sum()
birikimli toplamnp.cumsum(a)
komşu elemanların farkınp.diff(a)
en büyük değer ve konumua.max(), a.argmax()
Python
import numpy as np

prices = np.array([12.0, 45.5, 8.9, 120.0, 64.0])
qty = np.array([3, 1, 10, 1, 2])

revenue = prices * qty
discount = np.where(revenue > 100, 0.10, 0.0)
to_pay = (revenue * (1 - discount)).round(2)
print(revenue)
print(to_pay, to_pay.sum())

daily = np.array([520.0, 610.0, 580.0, 700.0, 655.0])
print(np.diff(daily))
print((np.diff(daily) / daily[:-1] * 100).round(1))
print(np.cumsum(daily))
▸ Beklenen çıktı
[ 36.   45.5  89.  120.  128. ]
[ 36.   45.5  89.  108.  115.2] 393.7
[ 90. -30. 120. -45.]
[17.3 -4.9 20.7 -6.4]
[ 520. 1130. 1710. 2410. 3065.]
100 manattan pahalı alışverişlere %10 indirim tek satırda uygulanır. Ardından günlük gelirin değişimi (manat ve yüzde olarak) ve birikimli toplam hesaplanır.

Broadcasting: farklı şekilli diziler

a * 10 yazdığında 10 sayısı sanki a ile aynı şekilde bir diziye “genişletilir”. Bu mekanizmaya broadcasting (yayınlama) denir ve yalnızca tek sayılarla değil, farklı şekilli dizilerle de çalışır. NumPy şekilleri sağdan sola karşılaştırır:

  1. Bir şeklin boyut sayısı azsa soluna 1'ler eklenir: (4,) → (1, 4).
  2. Her eksende iki uzunluk ya eşit olmalı ya da biri 1 olmalıdır.
  3. Uzunluğu 1 olan eksen, diğer dizinin uzunluğuna kadar “uzatılır” (bellekte kopyalanmadan); sonucun şekli her eksendeki en büyük değerdir.
  4. Herhangi bir eksende uzunluklar farklıysa ve hiçbiri 1 değilse ValueError oluşur.
ABSonuç
(3, 4)(4,)(3, 4)
(3, 1)(1, 4)(3, 4)
(2, 1, 5)(3, 1)(2, 3, 5)
(3, 4)(3,)hata: 4 ≠ 3

Broadcasting'in klasik bir uygulaması standartlaştırmadır: her sütundan ortalamasını çıkarıp standart sapmasına böleriz; böylece farklı ölçekteki öznitelikler (cm cinsinden boy, kg cinsinden ağırlık) karşılaştırılabilir hâle gelir. Şekli (4, 2) olan diziden şekli (2,) olan ortalama çıkarılır; kural 1 ve 3 işi yapar.

zᵢⱼ = (xᵢⱼ − μⱼ) / σⱼzᵢⱼ = (xᵢⱼ − μⱼ) / σⱼ
burada:
  • xᵢⱼi. örneğin j. özniteliği
  • μⱼ, σⱼj. sütunun ortalaması ve standart sapması
  • zᵢⱼstandartlaştırılmış değer (z-puanı): ortalaması 0, standart sapması 1
Python
import numpy as np

X = np.array([[170.0, 65.0],
              [160.0, 52.0],
              [182.0, 80.0],
              [175.0, 71.0]])
mean = X.mean(axis=0)
std = X.std(axis=0)
Z = (X - mean) / std
print(mean, std.round(3))
print(Z.round(2))
print(np.allclose(Z.mean(axis=0), 0), Z.std(axis=0).round(6))
▸ Beklenen çıktı
[171.75  67.  ] [ 8.012 10.173]
[[-0.22 -0.2 ]
 [-1.47 -1.47]
 [ 1.28  1.28]
 [ 0.41  0.39]]
True [1. 1.]

Bir sütun vektörüyle bir satır vektörünü birleştirince “her biri her biriyle” tablosu elde edilir. a[:, np.newaxis] (ya da a[:, None]) şekli (3,) olan diziyi (3, 1) sütununa çevirir:

Python
import numpy as np

a = np.arange(1, 4)
b = np.arange(1, 6)
table = a[:, np.newaxis] * b
print(table.shape)
print(table)

M = np.ones((3, 4))
v = np.array([10.0, 20.0, 30.0])
try:
    M + v
except ValueError as e:
    print('Error:', str(e).strip())
print((M + v[:, None])[:, 0])
▸ Beklenen çıktı
(3, 5)
[[ 1  2  3  4  5]
 [ 2  4  6  8 10]
 [ 3  6  9 12 15]]
Error: operands could not be broadcast together with shapes (3,4) (3,)
[11. 21. 31.]
Örnek 1: sonucun şeklini bul

Her işlemin sonuç şeklini bul ya da hata vereceğini göster: a) (5, 1, 3) + (4, 3); b) (8, 1) · (1, 6); c) (2, 3) + (2,); d) (256, 256, 3) · (3,).

Çözümü göster
a) (4, 3) → (1, 4, 3); eksen eksen: 5 ve 1 → 5, 1 ve 4 → 4, 3 ve 3 → 3. Sonuç (5, 4, 3).
b) 8 ve 1 → 8, 1 ve 6 → 6. Sonuç (8, 6): “her biri her biriyle” tablosu.
c) Sağdan: 3 ve 2 eşit değil ve hiçbiri 1 değil → ValueError.
d) Son eksenler 3 ve 3 uyuşuyor: sonuç (256, 256, 3). Böylece bir görüntünün her pikselindeki R, G, B kanalları üç farklı katsayıyla çarpılır.

Şekli değiştirmek

reshape aynı elemanları farklı bir şekilde gösterir; eleman sayısı değişmemelidir, boyutlardan biri ise -1 olabilir, NumPy onu kendisi hesaplar. .T devrik alır, ravel() diziyi tek boyuta indirir (mümkünse görünüm döndürür), np.vstack, np.hstack ve np.concatenate ise dizileri birleştirir.

Python
import numpy as np

a = np.arange(12)
print(a.reshape(3, 4))
print(a.reshape(2, -1).shape)
m = a.reshape(3, 4)
print(m.T.shape, m.ravel()[:5])
print(np.vstack([m[0], m[1]]))
print(np.concatenate([m, m], axis=1).shape)
▸ Beklenen çıktı
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
(2, 6)
(4, 3) [0 1 2 3 4]
[[0 1 2 3]
 [4 5 6 7]]
(3, 8)

Doğrusal cebir: np.linalg

Matrislerin çarpımı eleman eleman çarpma değildir. A * B karşılık gelen elemanları çarpar; A @ B (ya da np.dot) ise matematiksel matris çarpımını hesaplar: A'nın bir satırı B'nin bir sütunuyla skaler olarak çarpılır. Bunun için A'nın sütun sayısı B'nin satır sayısına eşit olmalıdır.

cᵢⱼ = ∑ₖ₌₁ⁿ aᵢₖ · bₖⱼ (m × n) @ (n × p) → (m × p)
burada:
  • aᵢₖA matrisinin i. satır, k. sütun elemanı
  • bₖⱼB matrisinin k. satır, j. sütun elemanı
  • nA'nın sütun ve B'nin satır sayısı (ortak)
Python
import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A * B)
print(A @ B)
print(np.dot(A, B).shape, (np.ones((2, 3)) @ np.ones((3, 5))).shape)
▸ Beklenen çıktı
[[ 5 12]
 [21 32]]
[[19 22]
 [43 50]]
(2, 2) (2, 5)
Kontrol: (A @ B)₀₀ = 1 · 5 + 2 · 7 = 19. Eleman eleman çarpım ise 1 · 5 = 5 verir.

Bir doğrusal denklem sistemi matris biçiminde Ax = b olarak yazılır. det A ≠ 0 ise sistemin tek bir çözümü vardır ve A'nın tersi mevcuttur: x = A⁻¹b. 2 × 2 bir matris için her şey elle hesaplanabilir:

A = [[a, b], [c, d]]: det A = a·d − b·c A⁻¹ = (1 / det A) · [[d, −b], [−c, a]]A = [[a, b], [c, d]]: det A = a·d − b·c A⁻¹ = (1 / det A) · [[d, −b], [−c, a]]
burada:
  • det Adeterminant; 0 ise matris tekildir ve tersi yoktur
  • A⁻¹ters matris: A · A⁻¹ = I (birim matris)
Örnek 2: defter ve kalem

3 defter ve 2 kalem 5,5 manat, 1 defter ve 4 kalem 3,5 manat. Bir defterin ve bir kalemin fiyatını matris yöntemiyle bul.

Çözümü göster
A = [[3, 2], [1, 4]], b = (5,5; 3,5).
det A = 3 · 4 − 2 · 1 = 10 ≠ 0; tek bir çözüm var.
A⁻¹ = (1/10) · [[4, −2], [−1, 3]].
x = A⁻¹b = (1/10) · (4 · 5,5 − 2 · 3,5; −5,5 + 3 · 3,5) = (1/10) · (15; 5) = (1,5; 0,5).
Defter 1,5 manat, kalem 0,5 manat. Kontrol: 3 · 1,5 + 2 · 0,5 = 5,5 ✓.

Üç bilinmeyenli bir sistemde (defter, kalem, cetvel) elle hesap uzar; NumPy ise onu tek satırda çözer:

Python
import numpy as np

A = np.array([[2.0, 1.0, 3.0],
              [1.0, 3.0, 1.0],
              [4.0, 2.0, 0.0]])
b = np.array([9.5, 5.0, 7.0])

x = np.linalg.solve(A, b)
print(x.round(2))
print(np.allclose(A @ x, b))
print(round(np.linalg.det(A), 2))
x_inv = np.linalg.inv(A) @ b
print(np.allclose(x, x_inv))
▸ Beklenen çıktı
[1.5 0.5 2. ]
True
-30.0
True
Defter 1,5, kalem 0,5, cetvel 2 manat. np.allclose, çözümün denklemleri sağladığını kontrol eder; ekrandaki determinant yuvarlanmıştır.

Özvektör, A matrisinin yalnızca uzattığı ya da kısalttığı, yönünü değiştirmediği sıfırdan farklı bir v vektörüdür; uzama katsayısı λ ise özdeğerdir. λ değerlerini karakteristik denklemden buluruz. Uygulamaları: temel bileşenler analizi (PCA), Google'ın PageRank algoritması, köprü ve binaların doğal titreşim frekansları, dinamik sistemlerin kararlılığı.

A · v = λ · v det(A − λ · I) = 0
burada:
  • vözvektör (v ≠ 0)
  • λözdeğer
  • Ibirim matris

İkinci eşitlik karakteristik denklemdir: (A − λI)v = 0 sisteminin sıfırdan farklı bir çözümü ancak determinant 0 olduğunda vardır.

Örnek 3: 2 × 2 bir matrisin özdeğerleri

A = [[2, 1], [1, 2]] matrisinin özdeğerlerini ve özvektörlerini bul.

Çözümü göster
det(A − λI) = (2 − λ)² − 1 = 0 → 2 − λ = ±1 → λ₁ = 1, λ₂ = 3.
λ = 3: (A − 3I)v = 0 → −v₁ + v₂ = 0 → v = (1; 1).
λ = 1: v₁ + v₂ = 0 → v = (1; −1).
Matris (1; 1) yönünü 3 kat uzatır, (1; −1) yönünü ise değiştirmez.
Python
import numpy as np

A = np.array([[2.0, 1.0], [1.0, 2.0]])
values, vectors = np.linalg.eigh(A)
print(values.round(4))
v = vectors[:, 1]
print(np.allclose(A @ v, values[1] * v))
print(round(np.linalg.det(A), 4), round(values.prod(), 4))
print(round(np.trace(A), 4), round(values.sum(), 4))
▸ Beklenen çıktı
[1. 3.]
True
3.0 3.0
4.0 4.0
Simetrik matrisler için eigh kullan: özdeğerleri artan sırada döndürür. Vektörler 1 uzunluğunda olacak şekilde normalleştirilir ama işaretleri kütüphaneye göre değişebilir.

Doğrusal cebir doğrudan makine öğrenmesine götürür. Noktalardan geçen en iyi doğruyu bulmak (en küçük kareler yöntemi) normal denkleme varır. X matrisine birlerden oluşan bir sütun eklersek sabit terim de otomatik olarak bulunur:

w = (Xᵀ X)⁻¹ Xᵀ y
burada:
  • Xn × d öznitelik matrisi (ilk sütun birlerden oluşur)
  • yhedef değerler vektörü
  • wkaresel hataların toplamını en küçük yapan katsayılar
Python
import numpy as np

rng = np.random.default_rng(7)
x = rng.uniform(0, 10, 50)
y = 2.5 * x + 1.0 + rng.normal(0, 1, 50)
X = np.column_stack([np.ones_like(x), x])
w = np.linalg.solve(X.T @ X, X.T @ y)
print(w.round(3))
w2, *_ = np.linalg.lstsq(X, y, rcond=None)
print(w2.round(3))
▸ Beklenen çıktı
[1.236 2.458]
[1.236 2.458]
Veri, y = 2,5x + 1 doğrusuna gürültü eklenerek üretildi; bulunan sabit terim ≈ 1,24, eğim ≈ 2,46. Uygulamada daha dayanıklı olan np.linalg.lstsq kullanılır; scikit-learn'deki LinearRegression da aynı problemi çözer.
Alıştırma

X matrisinin her sütununu döngüsüz, broadcasting ile [0; 1] aralığına getir (min–max normalleştirme): (X − sütunun minimumu) / (sütunun maksimumu − sütunun minimumu). Sonucu 2 ondalık basamağa yuvarlayarak yazdır.

Alıştırma · Python
import numpy as np

X = np.array([[2.0, 50.0, 1.0],
              [4.0, 80.0, 3.0],
              [6.0, 65.0, 2.0],
              [8.0, 95.0, 5.0]])
# min-max scale every column
▸ Beklenen çıktı
[[0.   0.   0.  ]
 [0.33 0.67 0.5 ]
 [0.67 0.33 0.25]
 [1.   1.   1.  ]]
Alıştırma

Pazarda: 2 kg elma + 1 kg armut + 1 kg üzüm = 9,5 manat; 1 kg elma + 2 kg armut + 1 kg üzüm = 10 manat; 1 kg elma + 1 kg armut + 3 kg üzüm = 13,5 manat. np.linalg.solve ile 1 kg elma, armut ve üzümün fiyatını bulup 2 ondalık basamakla yazdır, ardından matrisin determinantını 1 ondalık basamakla yazdır.

Alıştırma · Python
import numpy as np

# A: kilograms in each purchase, b: amounts paid (manat)
A = np.array([[2.0, 1.0, 1.0],
              [1.0, 2.0, 1.0],
              [1.0, 1.0, 3.0]])
b = np.array([9.5, 10.0, 13.5])
# solve A x = b and print the prices, then the determinant
▸ Beklenen çıktı
[2.  2.5 3. ]
7.0

Önemli noktalar

  • Vektörleştirme Python döngülerini C'de çalışan ufunc'larla değiştirir ve kodu onlarca, yüzlerce kat hızlandırır.
  • Broadcasting şekilleri sağdan karşılaştırır: uzunluklar eşit olmalı ya da biri 1 olmalıdır.
  • v[:, None] ve keepdims=True, vektörü sütuna çevirerek şekilleri uyumlu hâle getirir.
  • * eleman eleman çarpar, @ matris çarpımıdır: (m × n) @ (n × p) → (m × p).
  • Ax = b sistemini np.linalg.solve ile çöz; det A = 0 olduğunda tek bir çözüm yoktur.
  • Av = λv; özdeğerlerin toplamı ize, çarpımı determinanta eşittir.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
Şekli (4, 1) olan bir dizi ile şekli (3,) olan bir dizi toplanınca sonucun şekli ne olur?