ML Temelleri · Modül 0 · 30 dakika

NumPy hızlı başlangıç

Makine öğrenmesi kodunun büyük kısmı, sayı tablolarına aynı işlemi uygulamaktır. Her satırı ölçeklersin, iki tablonun farkını alırsın, sonuçları ortalarsın. Bunu Python döngüleriyle de yazabilirsin, ama kod uzar ve yavaşlar.

NumPy sayıları dizi (array) denen tek bir nesnede toplar. Bir işlemi bütün elemanlara tek bir ifadeyle uygular; buna vektörleştirme (vectorization)vektörleştirme (vectorization): Bir işlemi Python döngüsüyle eleman eleman yazmak yerine bütün diziye tek bir ifadeyle uygulama; döngü NumPy'ın hızlı koduna geçer. denir. Büyüklükleri farklı iki array’in aynı işleme nasıl girdiğini yayınlama (broadcasting)yayınlama (broadcasting): Şekilleri (shape) farklı dizileri sağ uçtan hizalayıp boyutu 1 olan eksenlerini uzatarak aynı işleme sokan kural. kuralı belirler.

Bu derste şunları yapacaksın:

  • Array kuracaksın.
  • Bir Python döngüsünü tek bir ifadeye çevireceksin.
  • Bir array’den parça seçeceksin.
  • Satırlar ve sütunlar boyunca özetleyeceksin.
  • İki array’in matris çarpımını (matrix product) alacaksın.
  • Broadcasting kuralını öğreneceksin.
  • Tekrarlanabilir rastgele sayılar üreteceksin.

Bu ders NumPy’ın yalnızca bu yolda gerekecek kısmını anlatır. Geri kalanı için NumPy belgelerine bakabilirsin.

Array, shape ve dtype

NumPy’ın array’i aynı türden sayıları yan yana saklar. Bir Python listesi ise her türden nesneyi tutabilir ve her eleman ayrı bir nesnedir.

Array kurmak

Bir array’i np.array ile bir listeden kurarsın. İç içe bir liste iki boyutlu bir array olur.

import numpy as npa = np.array([1.5, 2.0, 4.0])           # tek boyutluA = np.array([[1, 2, 3], [4, 5, 6]])    # iç içe liste: iki boyutluprint(a)    # → [1.5 2.  4. ]print(A)# → [[1 2 3]#    [4 5 6]]

np.zeros sıfırlardan, np.ones birlerden, np.arange ise 0’dan başlayan ardışık sayılardan array kurar. np.zeros((2, 3)) iki satır ve üç sütunluk bir array verir.

print(np.zeros((2, 3)))# → [[0. 0. 0.]#    [0. 0. 0.]]print(np.ones((2, 3)))# → [[1. 1. 1.]#    [1. 1. 1.]]print(np.arange(6))    # → [0 1 2 3 4 5]

Shape ve axis

Şekil (shape), array’in her boyutundaki eleman sayısını sıralayan bir demettir (tuple). a’nın shape’i (3,), yani tek boyut ve üç eleman. A’nın shape’i (2, 3), yani iki satır ve üç sütun.

a = np.array([1.5, 2.0, 4.0])A = np.array([[1, 2, 3], [4, 5, 6]])print(a.shape)    # → (3,)print(A.shape)    # → (2, 3)

Shape’teki her boyuta eksen (axis) denir. Axis 0 satırların sıralandığı yöndür, yani aşağı. Axis 1 sütunların sıralandığı yöndür, yani sağa. Bu numaraları sonraki bölümlerde yeniden kullanacaksın.

A = np.array([[1, 2, 3], [4, 5, 6]])# axis 0: satır sayısıprint(A.shape[0])    # → 2# axis 1: sütun sayısıprint(A.shape[1])    # → 3

dtype

dtype, elemanların türüdür. Bir array tek bir dtype taşır:

Kurulduğu sayılardtype
ondalıklı sayılarfloat64
tam sayılarint64 (bazı sistemlerde int32)
tam ve ondalıklı sayılar karışıkhepsi float64
print(np.array([1.5, 2.0, 4.0]).dtype)    # → float64print(np.array([1, 2, 3]).dtype)          # → int32   Linux ve macOS'ta int64print(np.array([1, 2.5, 3]).dtype)        # → float64

Bir tam sayı array’ine ondalıklı bir sayı yazarsan sayı sessizce kesilir. A[0, 0] = 2.7 yazdığında o eleman 2 olur.

A = np.array([[1, 2, 3], [4, 5, 6]])A[0, 0] = 2.7print(A[0, 0])    # → 2

dtype’ı kendin de değiştirebilirsin. astype, yeni bir dtype’ta bir kopya verir ve A’ya dokunmaz. True ve False da astype(int) ile 1 ve 0 olur.

A = np.array([[1, 2, 3], [4, 5, 6]])print(np.array([True, False, True]).astype(int))    # → [1 0 1]# float64 kopyaF = A.astype(float)print(F.dtype)    # → float64# kopyada ondalıklı sayı kesilmezF[0, 0] = 2.7print(F[0, 0])    # → 2.7print(A[0, 0])    # → 1
Kısa sorua = np.array([4, 5, 6]) için a[1] = 9.9 yazarsan a ne olur?

Cevabı göster

a, [4 9 6] olur: 9.9 sessizce kesilir. a bir int64 array'idir. İçine yazılan 9.9 tam sayıya kesilir ve NumPy hata vermez.

reshape ve hstack

reshape

reshape elemanları aynı sırayla tutup yalnızca shape’i değiştirir. NumPy elemanları satır satır okur ve yeni shape’e dizer. A.reshape(3, 2) şu matrisi verir:

[123456]\begin{bmatrix} 1 & 2 \\ 3 & 4 \\ 5 & 6 \end{bmatrix}
A = np.array([[1, 2, 3], [4, 5, 6]])print(A.reshape(3, 2))# → [[1 2]#    [3 4]#    [5 6]]print(A.reshape(3, 2).shape)    # → (3, 2)

Eleman sayısı değişemez. 2⋅3=62 \cdot 3 = 6 olduğundan A.reshape(4, 2) hata verir.

A = np.array([[1, 2, 3], [4, 5, 6]])try:    A.reshape(4, 2)except ValueError as e:    print(e)    # → cannot reshape array of size 6 into shape (4,2)

Verdiğin sayılardan birinin yerine -1 yazarsan NumPy onu kendisi hesaplar. A.reshape(3, -1) de (3, 2) verir.

A = np.array([[1, 2, 3], [4, 5, 6]])print(A.reshape(3, -1).shape)    # → (3, 2)print(A.reshape(-1, 2).shape)    # → (3, 2)

Array’leri yan yana koymak

Bir sütunu bir array’in yanına eklemek için np.hstack var. extra = np.array([[7], [8]]) olsun. np.hstack([A, extra]), (2, 3) ile (2, 1)’i birleştirir ve (2, 4) shape’inde bir array verir.

np.hstack için iki koşul var:

  • Satır sayıları eşit olmalıdır.
  • extra bir sütun, yani (2, 1) olmalıdır. (2,) shape’indeki bir array burada hata verir.
A = np.array([[1, 2, 3], [4, 5, 6]])extra = np.array([[7], [8]])print(np.hstack([A, extra]))# → [[1 2 3 7]#    [4 5 6 8]]try:    np.hstack([A, np.array([7, 8])])except ValueError as e:    print(type(e).__name__)    # → ValueError
Kısa soruA'nın shape'i (2, 3) ise A.reshape(3, -1) sonucunun shape'i nedir?

Cevabı göster

(3, 2) 6 eleman 3 satıra dizilince her satıra 2 eleman düşer.

Döngü yerine tek ifade

Vectorization, döngüyü Python’dan alıp NumPy’a verir ve hesabı hızlandırır.

Döngüyü tek ifadeye çevirmek

Dört sıcaklık değerini Celsius’tan Fahrenheit’a çevirelim. Formül şudur:

F=95 C+32F = \frac{9}{5}\,C + 32

Formül, Celsius değerini 9/5 ile çarpıp 32 ekler. Python’da bunu bir döngüyle yazarsın. NumPy’da ise formülü doğrudan array’e uygularsın.

c = np.array([0.0, 20.0, 37.0, 100.0])    # Celsiusf_loop = []for x in c:    f_loop.append(x * 9 / 5 + 32)         # döngü: eleman elemanf = c * 9 / 5 + 32                        # tek ifade, aynı sayılarprint(f)                                  # → [ 32.   68.   98.6 212. ]# iki sonuç aynı mı?print(np.allclose(f_loop, f))             # → True

İkinci biçimde döngü yok. Üç işlem de bütün array’e uygulanır:

  • c * 9 her elemanı 9 ile çarpar.
  • / 5 her elemanı 5’e böler.
  • + 32 her elemana 32 ekler.
c = np.array([0.0, 20.0, 37.0, 100.0])print(c * 9)           # → [  0. 180. 333. 900.]print(c * 9 / 5)       # → [  0.   36.   66.6 180. ]print(c * 9 / 5 + 32)  # → [ 32.   68.   98.6 212. ]

Eleman bazında (element-wise) işlemler

İki array arasındaki işlemler de aynı biçimde çalışır. NumPy aynı konumdaki elemanları çiftler. Buna eleman bazında işlem denir.

a = np.array([1, 2, 3])b = np.array([4, 5, 6])print(a + b)     # → [5 7 9]print(b - a)     # → [3 3 3]print(a * b)     # → [ 4 10 18]print(a ** 2)    # → [1 4 9]

** her elemanı verilen kuvvete yükseltir. a ** 2 her elemanın karesidir.

Listelerde + birleştirir, array’lerde toplar.

# iki liste: birleşirprint([1, 2] + [3, 4])                         # → [1, 2, 3, 4]# iki array: eleman bazında toplanırprint(np.array([1, 2]) + np.array([3, 4]))     # → [4 6]

Element-wise fonksiyonlar

np.sqrt, np.exp ve np.log gibi fonksiyonlar da element-wise çalışır. np.sqrt(a) her elemanın karekökünü alır.

print(np.sqrt(np.array([1, 4, 9])))    # → [1. 2. 3.]print(np.exp(np.array([0, 1])))        # → [1.         2.71828183]print(np.log(np.array([1, 10])))       # → [0.         2.30258509]

np.clip(b, 2, 5) değerleri 2 ile 5 arasında tutar. 2’den küçükleri 2’ye, 5’ten büyükleri 5’e çeker.

b = np.array([4, 5, 6])a = np.array([1, 2, 3])print(np.clip(b, 2, 5))    # → [4 5 5]print(np.clip(a, 2, 5))    # → [2 2 3]

* işareti de element-wise çalışır. a * b, iki array’in matris çarpımı değil, aynı konumdaki elemanların çarpımıdır. Matris çarpımının kendi operatörü var, onu aşağıda göreceksin.

P = np.array([[1, 2], [3, 4]])Q = np.array([[10, 20], [30, 40]])print(P * Q)# → [[ 10  40]#    [ 90 160]]

Hız farkı

Python döngüsü ile NumPy arasındaki fark şudur:

  • Python döngüsünün her turunda yorumlayıcı elemanın türüne bakar ve onu bir nesne olarak işler.
  • NumPy aynı türden sayılar üzerinde derlenmiş tek bir döngü çalıştırır.

Büyük array’lerde aynı işlem döngüden çok daha hızlı biter. Hız tek kazanç değildir. c * 9 / 5 + 32 formülle neredeyse harfi harfine aynıdır ve döngüden daha rahat okunur.

Kısa sorua = np.array([1, 2, 3]) ve b = np.array([4, 5, 6]) için a * b ne verir?

Cevabı göster

[ 4 10 18]: aynı konumdaki elemanlar çarpılır. * element-wise çalışır: 1 * 4, 2 * 5 ve 3 * 6. Sonuç, girdilerle aynı (3,) shape'indedir.

Array’den parça seçmek

Bir array’den eleman seçmeye indeksleme (indexing), bir aralık seçmeye dilimleme (slicing) denir. Python listelerindeki gibi sayma 0’dan başlar. İki boyutlu bir array’de her axis için bir indeks yazarsın, aralarına virgül koyarak.

Bu bölümde şu array’e bakacağız:

B=[01234567891011]B = \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix}

B = np.arange(12).reshape(3, 4) onu kurar. NumPy 0’dan 11’e kadar on iki sayıyı üç satır ve dört sütuna dizer.

B = np.arange(12).reshape(3, 4)print(B)# → [[ 0  1  2  3]#    [ 4  5  6  7]#    [ 8  9 10 11]]

Indexing

B[1, 2], indeksi 1 olan satırla indeksi 2 olan sütunun kesiştiği elemandır. Sayma 0’dan başladığı için bu, ikinci satırla üçüncü sütundur.

Tek bir indeks yazarsan o satırın hepsi gelir.

B = np.arange(12).reshape(3, 4)print(B[1, 2])    # → 6print(B[0])       # → [0 1 2 3]

Negatif indeks sondan sayar. B[-1, -1] sağ alt köşedir.

B = np.arange(12).reshape(3, 4)print(B[-1, -1])    # → 11print(B[-1])        # → [ 8  9 10 11]print(B[0, -1])     # → 3

Slicing

başla:bitir biçimindeki bir dilim (slice) başlangıcı dahil eder, bitişi etmez. Tek başına : hepsi demektir.

  • B[:, 1] her satırın indeksi 1 olan elemanını, yani ikinci sütunu seçer.
  • B[1:, :2] ikinci satırdan sona kadar olan satırlarla ilk iki sütunu seçer.
  • Bir axis için hiçbir şey yazmazsan o axis’in hepsi gelir. Bu yüzden B[0] ile B[0, :] aynıdır.
B = np.arange(12).reshape(3, 4)print(B[:, 1])      # → [1 5 9]print(B[1:, :2])# → [[4 5]#    [8 9]]print(B[0, :])      # → [0 1 2 3]print(B[1, 1:3])    # → [5 6]

Sayıyla ve dilimle seçmek

Bir axis’i tek bir sayıyla seçersen o axis shape’ten kalkar, dilimle seçersen kalır. B[0] için (4,), B[0:1] için (1, 4) çıkar.

B = np.arange(12).reshape(3, 4)print(B[1, 2].shape)     # → ()print(B[0].shape)        # → (4,)print(B[:, 1].shape)     # → (3,)print(B[1:, :2].shape)   # → (2, 2)print(B[0:1].shape)      # → (1, 4)

İndeks listesiyle seçmek

scores, altı öğrencinin sınav puanlarıdır. Seçeceğin indeksleri bir listeyle ya da bir tam sayı array’iyle verebilirsin. Sonuç listedeki sırayla dizilir ve bir indeks tekrar edebilir.

Satırları böyle seçince her satır bütün sütunlarıyla gelir. B[[2, 0]] sonucunun shape’i (2, 4)’tür.

B = np.arange(12).reshape(3, 4)scores = np.array([72, 45, 88, 61, 95, 53])print(scores[[0, 2, 2]])    # → [72 88 88]print(scores[[5, 0]])       # → [53 72]print(B[[2, 0]])# → [[ 8  9 10 11]#    [ 0  1  2  3]]print(B[[2, 0]].shape)      # → (2, 4)

Mantıksal maske (boolean mask)

Bir karşılaştırma, array’in her elemanı için bir True ya da False üretir. Bu array’e mantıksal maske (boolean mask) denir. Maskeyi indeks olarak verirsen yalnızca True olan elemanlar kalır.

scores = np.array([72, 45, 88, 61, 95, 53])mask = scores >= 60print(mask)             # → [ True False  True  True  True False]print(scores[mask])     # → [72 88 61 95]# aynı şey tek satırdaprint(scores[scores >= 60])    # → [72 88 61 95]

İki koşulu şöyle birleştirirsin:

  • & (ve) ya da | (veya) kullan.
  • Her koşulu parantez içine al.

Python’un and ile or sözcükleri array’lerde çalışmaz. NumPy bunun için hata verir.

scores = np.array([72, 45, 88, 61, 95, 53])print(scores[(scores > 50) & (scores < 90)])    # → [72 88 61 53]print(scores[(scores < 50) | (scores > 90)])    # → [45 95]try:    scores[(scores > 50) and (scores < 90)]except ValueError as e:    print(type(e).__name__)    # → ValueError

np.where

np.where(condition, x, y) koşulla aynı shape’te bir array verir. condition koşul demektir. Koşulun True olduğu her yerde x’ten, False olduğu yerde y’den eleman alır. x ile y birer sayı ya da array olabilir.

np.where(scores >= 60, scores, 0) geçenlerin puanını korur ve kalanları 0 yapar. np.where iki seçeneği de bütün elemanlar için hesaplar, sonra seçer.

scores = np.array([72, 45, 88, 61, 95, 53])print(np.where(scores >= 60, scores, 0))    # → [72  0 88 61 95  0]print(np.where(scores >= 60, 1, 0))         # → [1 0 1 1 1 0]
Kısa soruB = np.arange(12).reshape(3, 4) için B[:2, 3] ne verir?

Cevabı göster

[3 7] :2 yalnızca 0 ve 1 numaralı satırları seçer. 3 indeksi dördüncü sütundur ve bu iki satırda 3 ile 7'yi taşır.

Array’i indirgemek

Bir array’i daha küçük bir sonuca indiren işlemlere indirgeme (reduction) denir. Toplam, ortalama, en büyük ve en küçük değer birer indirgemedir. Fonksiyonları np.sum, np.mean, np.max ve np.min’dir.

Burada yine şu array ile çalışalım:

A=[8314296350118]A = \begin{bmatrix} 8 & 3 & 1 & 4 \\ 2 & 9 & 6 & 3 \\ 5 & 0 & 11 & 8 \end{bmatrix}

Axis vermeden özetlemek

axis vermezsen bütün elemanlar tek bir sayıya iner. Sonucun shape’i ()’dir.

A = np.array([[8, 3, 1, 4], [2, 9, 6, 3], [5, 0, 11, 8]])print(np.sum(A))     # → 60print(np.mean(A))    # → 5.0print(np.max(A))     # → 11print(np.min(A))     # → 0

Axis boyunca özetlemek

axis argümanı, shape’ten hangi axis’in kalkacağını söyler. A’nın shape’i (3, 4).

  • axis=0 verirsen axis 0 kalkar. Satırlar boyunca ilerleyip her sütunu kendi içinde toplarsın. Geriye (4,) kalır.
  • axis=1 verirsen axis 1 kalkar. Her satırı kendi içinde toplarsın. Geriye (3,) kalır.
A = np.array([[8, 3, 1, 4], [2, 9, 6, 3], [5, 0, 11, 8]])# 8+2+5, 3+9+0, 1+6+11, 4+3+8print(np.sum(A, axis=0))    # → [15 12 18 15]# 8+3+1+4, 2+9+6+3, 5+0+11+8print(np.sum(A, axis=1))    # → [16 20 24]print(np.sum(A, axis=0).shape)    # → (4,)print(np.sum(A, axis=1).shape)    # → (3,)

np.mean, np.max ve np.min aynı axis argümanını alır. np.sum(A, axis=0) yerine A.sum(axis=0) yazmak da aynı işi görür.

A = np.array([[8, 3, 1, 4], [2, 9, 6, 3], [5, 0, 11, 8]])print(np.mean(A, axis=0))    # → [5. 4. 6. 5.]print(np.max(A, axis=1))     # → [ 8  9 11]print(A.sum(axis=0))         # → [15 12 18 15]

Aşağıda işlemi ve axis’i seç ve sonucun shape’ine bak.

axis_explorer

np.sum(A, axis=0)

A · shape (3, 4)

axis 1 →axis 0
8
3
1
4
2
9
6
3
5
0
11
8

sonuç · shape (4,)

print çıktısı

[15 12 18 15]

sonuç[0] = 8 + 2 + 5 = 15 axis 0 shape'ten kalkar: (3, 4) → (4,)

Koşulu sağlayanları saymak

Boolean array’lerde True 1, False 0 sayılır. Bu yüzden bir maskenin toplamı, koşulu sağlayan eleman sayısıdır.

scores = np.array([72, 45, 88, 61, 95, 53])print(np.sum(scores >= 60))    # → 4

Karelerin toplamı

Karelerin toplamı iki adımdır: önce her elemanın karesini al, sonra kareleri topla. Önce element-wise **, sonra bir reduction gelir.

S=∑i=1nxi2S = \sum_{i=1}^{n} x_i^2

Formül, her xix_i‘nin karesini alıp hepsini toplar. Toplamın karesi farklı bir sayıdır.

def sum_of_squares(x: np.ndarray) -> float:    return float(np.sum(x ** 2))x = np.array([3.0, -4.0])print(sum_of_squares(x))    # → 25.0# toplamın karesi: yanlışprint(np.sum(x) ** 2)       # → 1.0
Kısa soruA'nın shape'i (3, 4) ise np.sum(A, axis=0) sonucunun shape'i nedir?

Cevabı göster

(4,) Axis 0 shape'ten kalkar ve geriye 4 kalır: her sütun kendi içinde toplanır, dört sütun dört sayı verir.

Matris çarpımı

Matris çarpımının operatörü @ işaretidir. Bu bölümde A, v ve D array’leriyle çalışacağız.

Shape kuralı

Shape’leri (m, n) ve (n, k) olan iki array’i çarpabilirsin:

  • İç boyutlar (n) eşit olmalıdır.
  • Sonuç (m, k) shape’inde çıkar.
  • Sonucun her elemanı şöyle çıkar: soldaki bir satırı sağdaki bir sütunla karşılıklı çarp, çarpımları topla.
left = np.ones((2, 3))right = np.ones((3, 4))print((left @ right).shape)    # → (2, 4)

İç boyutlar uyuşmazsa NumPy hata verir. D @ v için (3, 2) ve (3,) karşılaşır, 2 ile 3 eşit değildir.

D = np.array([[1, 0], [0, 1], [1, 1]])v = np.array([2, 0, 1])try:    D @ vexcept ValueError as e:    print(type(e).__name__)    # → ValueError

@ yerine * yazma. A * D bu shape’lerde hata verir: shape’ler farklı olduğu için aynı konumdaki elemanlar çiftlenemez.

A = np.array([[1, 2, 3], [4, 5, 6]])D = np.array([[1, 0], [0, 1], [1, 1]])try:    A * Dexcept ValueError as e:    print(e)    # → operands could not be broadcast together with shapes (2,3) (3,2)

Hata mesajındaki broadcast kuralını son bölümde göreceksin.

Matris ile vektör

A’nın yanına v = np.array([2, 0, 1]) koyalım. A @ v için A’nın her satırı v ile karşılıklı çarpılıp toplanır:

1⋅2+2⋅0+3⋅1=5,4⋅2+5⋅0+6⋅1=141 \cdot 2 + 2 \cdot 0 + 3 \cdot 1 = 5, \qquad 4 \cdot 2 + 5 \cdot 0 + 6 \cdot 1 = 14

Tek boyutlu v burada sütun gibi davranır. Eşleşen boyut sonuçtan kalkar.

A = np.array([[1, 2, 3], [4, 5, 6]])v = np.array([2, 0, 1])print(A @ v)            # → [ 5 14]print((A @ v).shape)    # → (2,)# ilk satırı elle: 1*2 + 2*0 + 3*1print(np.sum(A[0] * v))    # → 5

Bir veri matrisini bir vektörle çarpıp her satır için tek bir sayı üretmek, Model, parametre, kayıp dersinde karşına çıkacak.

İki matris

D = np.array([[1, 0], [0, 1], [1, 1]]) olsun. A @ D için shape’ler (2, 3) ve (3, 2) olur; sonuç (2, 2) çıkar.

A = np.array([[1, 2, 3], [4, 5, 6]])D = np.array([[1, 0], [0, 1], [1, 1]])print(A @ D)# → [[ 4  5]#    [10 11]]# A'nın 0. satırı ile D'nin 1. sütunuprint(np.sum(A[0] * D[:, 1]))    # → 5

Devrik (transpose)

A.T, A’nın devriğidir (transpose). Satırları sütun olur ve (2, 3) shape’i (3, 2)’ye döner. Matematikte A⊤A^\top diye yazılır:

A⊤=[142536]A^\top = \begin{bmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{bmatrix}

A.T @ A’nın shape’i bu yüzden (3, 3)’tür. Tek boyutlu bir array’de .T bir şey yapmaz. v.T yine (3,)’tür.

A = np.array([[1, 2, 3], [4, 5, 6]])v = np.array([2, 0, 1])print(A.T)# → [[1 4]#    [2 5]#    [3 6]]print((A.T @ A).shape)    # → (3, 3)print(v.T.shape)          # → (3,)

Doğrusal denklem sistemi

Doğrusal bir denklem sistemini M @ x = c biçiminde çözmek için np.linalg.solve(M, c) var. M = np.array([[2, 1], [1, 3]]) ve c = np.array([4, 7]) için [1. 2.] verir. Sonuç sistemi sağlar, çünkü 2⋅1+1⋅2=42 \cdot 1 + 1 \cdot 2 = 4 ve 1⋅1+3⋅2=71 \cdot 1 + 3 \cdot 2 = 7.

M = np.array([[2, 1], [1, 3]])c = np.array([4, 7])x = np.linalg.solve(M, c)print(x)        # → [1. 2.]# sistemi sağlıyor mu?print(M @ x)    # → [4. 7.]

Bunu Doğrusal regresyon dersinde kullanacaksın.

Kısa soruX'in shape'i (5, 3), W'nin shape'i (3, 2) ise X @ W sonucunun shape'i nedir?

Cevabı göster

(5, 2) İç boyutlar (3 ile 3) eşleşir ve sonuçtan kalkar. Geriye soldaki array'in satır sayısı 5 ile sağdaki array'in sütun sayısı 2 kalır.

Farklı shape’ler bir arada

Broadcasting, shape’leri farklı iki array’i birlikte işleme sokan kuraldır. Element-wise işlemler aynı shape’i istiyor gibi görünür, ama A + 10 de çalışır. 10 her elemana eklenir.

Broadcasting kuralı

Sonuç shape’ini üç adımda bulursun:

  1. Shape’leri sağ uçtan hizala: son boyut son boyutla, bir önceki bir öncekiyle karşılaşır.
  2. Kısa shape’in soluna 1’ler ekle.
  3. Her boyut çiftinde büyüğünü al: sonucun o boyutu odur.

Her boyut çifti ya eşit olmalı ya da çiftten biri 1 olmalı. 1 olan boyut, öbürünün boyutuna uyacak kadar uzar.

A = np.array([[1, 2, 3], [4, 5, 6]])r = np.array([10, 20, 30])# A: (2, 3), r: (3,) → (1, 3) → (2, 3)print(A.shape, r.shape)    # → (2, 3) (3,)print((A + r).shape)       # → (2, 3)

Aşağıda bir shape çifti seç ve küçük array’in nasıl uzadığına bak.

broadcast_stretch
arrayshapeaxis 0axis 1
a(3, 4)34
b(4,)14
sonuç(3, 4)34
1 → 3 uzareşit

a · (3, 4)

0
1
2
3
4
5
6
7
8
9
10
11

b · (4,)

10
20
30
40

sonuç · (3, 4)

b (4,) sağdan hizalanınca (1, 4) · b: axis 0 boyunca 1 → 3 uzar · sonuç (3, 4) sonuç[1, 2] = a[1, 2] + b[2] = 6 + 30 = 36

Sayı, satır ve sütun

A + 10 için shape’ler (2, 3) ve () olur. 10 her elemana eklenir ve sonuç (2, 3) kalır.

A = np.array([[1, 2, 3], [4, 5, 6]])print(A + 10)# → [[11 12 13]#    [14 15 16]]

A’nın yanına r = np.array([10, 20, 30]) koyalım. A + r, tek satırlık rr‘yi AA‘nın iki satırına da ekler:

[123456]+[102030]=[112233142536]\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix} + \begin{bmatrix} 10 & 20 & 30 \end{bmatrix} = \begin{bmatrix} 11 & 22 & 33 \\ 14 & 25 & 36 \end{bmatrix}
A = np.array([[1, 2, 3], [4, 5, 6]])r = np.array([10, 20, 30])print(A + r)# → [[11 22 33]#    [14 25 36]]

s = np.array([[2], [3]]) için shape’ler (2, 3) ve (2, 1) olur. A * s birinci satırı 2 ile, ikinciyi 3 ile çarpar.

A = np.array([[1, 2, 3], [4, 5, 6]])s = np.array([[2], [3]])print(A * s)# → [[ 2  4  6]#    [12 15 18]]

Uyuşmayan shape’ler

t = np.array([1, 2]) için A + t hata verir. t’nin shape’i (2,) olduğundan hizalanınca (1, 2) olur. A’nın son boyutu 3, t’ninki 2’dir.

A = np.array([[1, 2, 3], [4, 5, 6]])t = np.array([1, 2])try:    A + texcept ValueError as e:    print(e)    # → operands could not be broadcast together with shapes (2,3) (2,)

Çözüm, t’yi bir sütuna çevirmektir. t.reshape(2, 1) (ya da t.reshape(-1, 1)) (2, 1) shape’indedir. A + t.reshape(2, 1), A’nın ilk satırına 1, ikinci satırına 2 ekler.

A = np.array([[1, 2, 3], [4, 5, 6]])t = np.array([1, 2])print(t.reshape(2, 1).shape)    # → (2, 1)print(A + t.reshape(2, 1))# → [[2 3 4]#    [6 7 8]]

İki yönde birden uzamak

Her iki array’de de 1 olan boyutlar olabilir. O zaman ikisi de kendi 1’ini uzatır: (4, 1) ile (1, 3) toplanınca (4, 3) çıkar.

İpucu

Aşağıdaki çıktıya bakmadan, shape’leri (4, 1) ve (1, 3) olan iki array’in toplamının shape’ini bul, sonra çıktıyla karşılaştır.

col = np.array([[0], [10], [20], [30]])    # (4, 1)row = np.array([[1, 2, 3]])                # (1, 3)print((col + row).shape)    # → (4, 3)print(col + row)# → [[ 1  2  3]#    [11 12 13]#    [21 22 23]#    [31 32 33]]

Sütunları merkezleme

Broadcasting’in sık kullanılan bir örneği, veri matrisinin sütunlarını merkezlemektir. Her sütundan o sütunun ortalamasını çıkarırsın, böylece her sütunun ortalaması 0 olur.

X’in shape’i (n, d), ortalamaların shape’i (d,)’dir. (d,) hizalanınca (1, d) olur ve her satıra uygulanır.

def center_columns(X: np.ndarray) -> np.ndarray:    return X - X.mean(axis=0)X = np.array([[1.0, 10.0], [3.0, 20.0], [5.0, 30.0]])print(X.mean(axis=0))    # → [ 3. 20.]print(center_columns(X))# → [[ -2. -10.]#    [  0.   0.]#    [  2.  10.]]

(3,) ile (3, 1) farkı

Dikkat

(3,) ile (3, 1) aynı üç sayıyı taşır ama farklı array’lerdir. İkisini birbirinden çıkarırsan (3,) hizalanınca (1, 3) olur ve sonuç (3, 3) çıkar.

Hata yok, ama üç yerine dokuz sayı çıkar. Bu, NumPy kodunda sık rastlanan sessiz hatalardan biridir. Bir işlemden önce .shape’e bak.

u = np.array([1, 2, 3])        # (3,)u_col = u.reshape(3, 1)        # (3, 1)print((u - u_col).shape)       # → (3, 3)print(u - u_col)# → [[ 0  1  2]#    [-1  0  1]#    [-2 -1  0]]
Kısa soruA'nın shape'i (3, 4), r'nin shape'i (3,) ise A + r ne yapar?

Cevabı göster

Hata verir: sağ uçtan hizalanınca 4 ile 3 karşılaşır ve ikisi de 1 değildir. r'nin her satıra bir sayı eklemesi için shape'inin (3, 1) olması gerekir. (3,) shape'i ise sağ uçtan hizalanınca A'nın son boyutuyla uyuşmaz.

Rastgele sayılar

Deneme verisi için rastgele sayılar gerekir, ama deneyin tekrar çalıştırılabilmesi için bu sayıların aynı çıkması gerekir.

Tohum (seed)

Önce rng = np.random.default_rng(0) ile bir rastgele sayı üreteci kurarsın. Parantezin içindeki sayı tohumdur (seed).

Aynı seed ile kurulan üreteç hep aynı sayıları verir, böylece sonuçlar tekrarlanabilir olur. rng.random(2), 0 ile 1 arasında iki sayı verir.

rng_a = np.random.default_rng(0)rng_b = np.random.default_rng(0)rng_c = np.random.default_rng(1)print(rng_a.random(2))    # → [0.63696169 0.26978671]# aynı seed, aynı sayılarprint(rng_b.random(2))    # → [0.63696169 0.26978671]# başka seed, başka sayılarprint(rng_c.random(2))    # → [0.51182162 0.9504637 ]

Eski np.random.seed ve np.random.rand tek bir küresel durumu paylaşır. Bu yolda hep kendi rng nesneni kullanacaksın.

Üreteçten sayı çekmek

Üreteç üç işe yarar:

İfadeVerdiği
rng.normal(size=(4, 2))ortalaması 0 ve standart sapması (standard deviation) 1 olan normal dağılımdan (4, 2) shape’inde bir array
rng.random(3)0 ile 1 arasında üç sayı
rng.permutation(5)0’dan 4’e kadar sayıların rastgele bir sıralaması, (5,) shape’inde
rng = np.random.default_rng(0)print(rng.normal(size=(4, 2)).shape)    # → (4, 2)print(rng.random(3))                    # → [0.54362499 0.93507242 0.81585355]print(rng.permutation(5))               # → [2 1 3 4 0]

Üreteç her çağrıda kaldığı yerden devam eder. Bu yüzden değerler çağrıların sırasına bağlıdır.

Kısa soruBir deneyi iki kez çalıştırıyorsun ve iki seferde de rng = np.random.default_rng(42) yazıyorsun. rng.random(3) ne verir?

Cevabı göster

İki seferde de aynı üç sayıyı. Aynı seed ile kurulan üreteç aynı sayıları aynı sırayla verir. Sonuçlar bu yüzden tekrarlanabilir.

Özet

  • Shape her axis’in eleman sayısıdır, dtype elemanların türüdür.
  • reshape elemanları aynı sırayla yeni bir shape’e dizer.
  • Vectorization, bir işlemi eleman eleman yazmak yerine bütün array’e tek bir ifadeyle uygular.
  • +, -, *, ** işlemleri ile np.sqrt ve np.clip gibi fonksiyonlar element-wise çalışır.
  • Indexing tek bir elemanı, slicing bir aralığı seçer. Boolean mask koşula uyan elemanları seçer, np.where koşula göre iki değerden birini alır.
  • np.sum ve np.mean gibi reduction’lar bir array’i küçültür. axis verirsen yalnızca o axis shape’ten kalkar.
  • Matris çarpımının operatörü @ işaretidir, A.T devriktir.
  • Broadcasting’de shape’ler sağ uçtan hizalanır ve her boyut çifti eşit ya da 1 olmalıdır. (3,) ile (3, 1) farklı şeylerdir.
  • rng = np.random.default_rng(seed) ile kurulan üreteç aynı seed’de aynı sayıları verir.

Terimler

TürkçeEnglish
diziarray
şekilshape
eksenaxis
demettuple
standart sapmastandard deviation
eleman bazındaelement-wise
vektörleştirmevectorization
yayınlamabroadcasting
matris çarpımımatrix product
devriktranspose
indekslemeindexing
dilimlemeslicing
mantıksal maskeboolean mask
indirgemereduction
tohumseed

Şimdi sıra sende

  1. Döngüyü vektörleştirZorluk: KolayBaşla →
  2. Sütunları merkezleZorluk: KolayBaşla →
İlk problem: Döngüyü vektörleştir →

Notların

Metinden bir bölüm seçip Not al'a basarak da ekleyebilirsin.

Geri bildirim

Tür