Ana içeriğe geç
Kesit Python
İleri · Makine öğrenmesi

Sinir ağlarına (neural networks) giriş: numpy'dan PyTorch'a

Nöron, katman ve aktivasyon; ileri yayılım ve kayıp; numpy ile 2 katmanlı ağda geri yayılım ve gradyan inişi; mini-batch, öğrenme oranı, eğitim eğrisi; dropout, erken durdurma ve L2; scikit-learn MLPClassifier ile tablo verisi ve gradient boosting karşılaştırması; PyTorch gösterimi; CNN, RNN, transformer ve transfer öğrenmeye bakış.

Uygulamada aç, 21 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

Sinir ağı: katman katman öğrenen bir fonksiyon

Bir el yazısı rakam görüntüsü 64 pikselden oluşur. Hangi pikselin hangi rakamı işaret ettiğini kural olarak yazamazsın; bir lojistik regresyon da pikselleri tek tek ağırlıklandırmakla sınırlı kalır. Sinir ağı (neural network) ise ara katmanlarda kendi özelliklerini öğrenir: ilk katman kenarları, sonraki katman kenarlardan oluşan şekilleri. Bu, görüntü, ses ve metinde derin öğrenmeyi (deep learning) vazgeçilmez yapan özelliktir.

Bu derste sinir ağını önce numpy ile sıfırdan kuracaksın (ileri yayılım, kayıp, geri yayılım, gradyan inişi), sonra scikit-learn'ün MLPClassifier'ı ile Ilgaz Bank verisinde ağaç modelleriyle karşılaştıracak, en sonda PyTorch kodunu okuyacaksın. Makine öğrenmesine giriş ve Ön işleme, pipeline ve model değerlendirme derslerindeki bölme, ölçekleme ve sızıntı kuralları burada da aynen geçerli.

Nöron, katman, aktivasyon

Bir nöron girdilerin ağırlıklı toplamını alır, bir sapma (bias) ekler ve sonucu bir aktivasyon fonksiyonundan geçirir: a = f(w·x + b). Bir katman aynı girdiye bakan nöronlar kümesidir ve matris çarpımıyla tek satırda hesaplanır: A = f(X @ W + b). X'in şekli (örnek, girdi), W'nin (girdi, nöron), çıktınınki (örnek, nöron).

AktivasyonFormülNerede?
ReLUmax(0, z)Gizli katmanların varsayılanı; hızlı, gradyanı sönmez
Sigmoid1 / (1 + e−z)İkili sınıflandırma çıktısı (olasılık)
Tanh(ez − e−z) / (ez + e−z)−1..1 arası; RNN'lerde
Softmaxezi / Σ ezjÇok sınıflı çıktı; olasılıklar 1'e toplanır

Aktivasyon neden şart? Doğrusal katmanları üst üste koymak yine doğrusal bir fonksiyondur: (X @ W1) @ W2 = X @ (W1 @ W2). Doğrusal olmayan aktivasyon olmadan 10 katmanlı ağ tek bir lojistik regresyondan güçlü değildir.

import numpy as np

def relu(z):
    return np.maximum(0, z)

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

def softmax(z):
    z = z - z.max(axis=1, keepdims=True)        # taşmayı önler, sonucu değiştirmez
    e = np.exp(z)
    return e / e.sum(axis=1, keepdims=True)

z = np.array([[-2.0, 0.0, 3.0]])
print("relu   :", relu(z))
print("sigmoid:", sigmoid(z).round(3))
print("softmax:", softmax(z).round(3), "toplam", softmax(z).sum())

İleri yayılım ve kayıp

İki katmanlı bir ağ (bir gizli katman) için ileri yayılım (forward pass): Z1 = X @ W1 + b1, A1 = relu(Z1), Z2 = A1 @ W2 + b2, p = sigmoid(Z2). Tahminin ne kadar kötü olduğunu kayıp fonksiyonu (loss) söyler: regresyonda ortalama kare hata (MSE), ikili sınıflandırmada ikili çapraz entropi (binary cross-entropy): −ortalama[y·log p + (1 − y)·log(1 − p)]. Doğru sınıfa düşük olasılık veren tahmin çok ağır cezalandırılır.

rng = np.random.default_rng(0)
X = rng.normal(size=(5, 3))                    # 5 örnek, 3 özellik
W1, b1 = rng.normal(0, 0.5, (3, 4)), np.zeros(4)   # 4 gizli nöron
W2, b2 = rng.normal(0, 0.5, (4, 1)), np.zeros(1)

A1 = relu(X @ W1 + b1)
p = sigmoid(A1 @ W2 + b2)
print("şekiller:", X.shape, A1.shape, p.shape)
y = np.array([[1], [0], [1], [0], [1]])
kayip = -np.mean(y * np.log(p) + (1 - y) * np.log(1 - p))
print("ikili çapraz entropi:", round(float(kayip), 4))

Gradyan inişi ve geri yayılım

Öğrenme, kaybı azaltacak yönde ağırlıkları küçük adımlarla değiştirmektir: W ← W − η · ∂L/∂W. η öğrenme oranıdır (learning rate). Gradyanları her ağırlık için ayrı ayrı türevleyerek bulmak yerine geri yayılım (backpropagation) zincir kuralını çıktıdan girdiye doğru uygular. Sigmoid + çapraz entropi ikilisinin güzel bir özelliği var: çıktıdaki hata yalnızca p − y'dir.

  1. dZ2 = (p − y) / n
  2. dW2 = A1ᵀ @ dZ2, db2 = dZ2.sum(axis=0)
  3. dA1 = dZ2 @ W2ᵀ, ReLU'nun türevi: dZ1 = dA1 · (Z1 > 0)
  4. dW1 = Xᵀ @ dZ1, db1 = dZ1.sum(axis=0)
  5. Her parametreyi param −= η · grad ile güncelle.
from sklearn.datasets import make_moons

X, y = make_moons(n_samples=400, noise=0.2, random_state=0)
y = y.reshape(-1, 1)
rng = np.random.default_rng(1)
W1, b1 = rng.normal(0, 1, (2, 16)), np.zeros(16)
W2, b2 = rng.normal(0, 1, (16, 1)), np.zeros(1)
eta, n = 0.5, len(X)
for epoch in range(2001):
    Z1 = X @ W1 + b1; A1 = relu(Z1); p = sigmoid(A1 @ W2 + b2)       # ileri
    dZ2 = (p - y) / n                                                # geri
    dW2, db2 = A1.T @ dZ2, dZ2.sum(axis=0)
    dZ1 = (dZ2 @ W2.T) * (Z1 > 0)
    dW1, db1 = X.T @ dZ1, dZ1.sum(axis=0)
    W1 -= eta * dW1; b1 -= eta * db1; W2 -= eta * dW2; b2 -= eta * db2
    if epoch % 500 == 0:
        kayip = -np.mean(y * np.log(p + 1e-12) + (1 - y) * np.log(1 - p + 1e-12))
        print(f"epoch {epoch:4d}  kayıp {kayip:.3f}  doğruluk {((p > 0.5) == y).mean():.3f}")

İki ay şeklindeki veri doğrusal ayrılamaz; lojistik regresyon %85 civarında takılır, gizli katmanlı ağ %95'i geçer. Gizli katman ayırıcı eğriyi kendi öğrendi.

Mini-batch, öğrenme oranı, epoch

Yukarıda her adımda tüm veriyi kullandık (tam yığın). Büyük veride her adımda küçük bir parça (mini-batch, ör. 32 ya da 256 örnek) kullanılır: adımlar ucuzlar ve gürültü yerel tuzaklardan kaçmaya yardım eder. Verinin tamamının bir kez görülmesine epoch denir; her epoch başında veri karıştırılır.

Eğitim eğrisinde gördüğünOlası nedenNe yap?
Kayıp patlıyor, nan oluyorÖğrenme oranı çok büyükη'yı 10'a böl; girdileri ölçekle
Kayıp çok yavaş iniyorη çok küçük ya da ölçeksiz girdiη'yı artır, Adam optimizasyonu kullan
Eğitim kaybı iniyor, doğrulama yükseliyorAşırı öğrenme (overfitting)Erken durdurma, dropout, L2, daha çok veri
İkisi de yüksek ve düzModel çok küçükNöron/katman ekle, daha uzun eğit

Aşırı öğrenmeye karşı: düzenlileştirme, dropout, erken durdurma

def dropout(A, p, rng, egitim=True):
    if not egitim or p == 0:
        return A
    maske = rng.random(A.shape) >= p
    return A * maske / (1 - p)

A = np.ones((2, 8))
r = np.random.default_rng(0)
print(dropout(A, 0.5, r))
print("ortalama korunur mu?", dropout(np.ones((1000, 100)), 0.5, r).mean().round(2))

scikit-learn MLPClassifier ile tablo verisi

Küçük ve orta boy tablo verisinde PyTorch'a gerek yok: MLPClassifier ve MLPRegressor Adam optimizasyonu, L2 (alpha), mini-batch ve erken durdurmayı hazır sunar. İki kural: girdileri mutlaka ölçekle (Pipeline içinde) ve random_state ver (başlangıç ağırlıkları rastgeledir).

from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

Xd, yd = load_digits(return_X_y=True)          # 8x8 piksel, 10 rakam
Xa, Xb, ya, yb = train_test_split(Xd, yd, test_size=0.25, random_state=0, stratify=yd)
mlp = make_pipeline(StandardScaler(), MLPClassifier(hidden_layer_sizes=(64,), max_iter=300, random_state=0))
mlp.fit(Xa, ya)
print("rakam doğruluğu:", round(mlp.score(Xb, yb), 3), " epoch:", mlp[-1].n_iter_)

Ilgaz Bank'ın "ikinci yarıda kim çok harcayacak?" tablosunda (Özellik mühendisliği: ham veriden modele hazır özelliğe dersindeki özellikler) ise tek gizli katmanlı MLP ile HistGradientBoostingClassifier neredeyse aynı AUC'yi (≈0.82) veriyor; boosting ölçekleme istemez, daha hızlı eğitilir ve ayarı daha az hassastır. Mini projede bunu kendin ölçeceksin.

PyTorch: tensör, nn.Module, eğitim döngüsü, DataLoader

Gerçek derin öğrenme projelerinde PyTorch kullanılır: GPU üzerinde hesap yapar ve gradyanları otomatik türev (autograd) ile kendisi bulur. Elle yazdığın geri yayılımı loss.backward() tek satırda yapar. PyTorch tarayıcıda çalışmaz; kodu oku ve kendi bilgisayarında dene.

import torch
from torch import nn
from torch.utils.data import DataLoader, TensorDataset

X_t = torch.tensor(X_egitim, dtype=torch.float32)
y_t = torch.tensor(y_egitim, dtype=torch.float32).unsqueeze(1)
yukleyici = DataLoader(TensorDataset(X_t, y_t), batch_size=32, shuffle=True)

class HarcamaAgi(nn.Module):
    def __init__(self, girdi):
        super().__init__()
        self.katmanlar = nn.Sequential(
            nn.Linear(girdi, 16), nn.ReLU(), nn.Dropout(0.2),
            nn.Linear(16, 1))                     # sigmoid kayıp içinde
    def forward(self, x):
        return self.katmanlar(x)

model = HarcamaAgi(X_t.shape[1])
kayip_fn = nn.BCEWithLogitsLoss()
opt = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)
for epoch in range(50):
    model.train()                                 # dropout açık
    for xb, yb in yukleyici:
        opt.zero_grad()                           # eski gradyanları sil
        kayip = kayip_fn(model(xb), yb)
        kayip.backward()                          # geri yayılım
        opt.step()                                # ağırlık güncelle
model.eval()                                      # dropout kapalı
with torch.no_grad():
    olasilik = torch.sigmoid(model(torch.tensor(X_test, dtype=torch.float32)))
Kendi bilgisayarında denepip install torch (ekran kartın varsa pytorch.org'daki CUDA komutunu kullan). Döngüdeki üç satırlık ritüel (zero_grad, backward, step) her PyTorch projesinde aynıdır; model.train() ve model.eval() geçişini unutmak dropout'un tahminde açık kalmasına yol açar.

CNN, RNN, transformer ve transfer öğrenme

MimariFikirTipik kullanım
CNN (evrişimli ağ)Küçük filtreler görüntü üzerinde kayar; komşu piksellerden yerel desen öğrenirGörüntü sınıflandırma, belge tarama, kalite kontrol
RNN / LSTMDiziyi adım adım okur, gizli durum taşırEski nesil dil ve zaman serisi modelleri
TransformerDikkat (attention) ile her öğe diğer tüm öğelere bakar; paralel eğitilirDil modelleri (GPT, BERT), çeviri, artık görüntü de

Transfer öğrenme: milyonlarca görüntü ya da metinle önceden eğitilmiş bir modeli alıp son katmanlarını kendi küçük verinle ince ayarlarsın (fine-tuning). 500 etiketli fatura görüntüsüyle sıfırdan CNN eğitilmez, ama hazır bir ResNet'in son katmanı eğitilebilir. Metin tarafını Metin işleme ve NLP: Türkçe metinden modele ve Embedding ve vektör arama: anlamsal arama ve RAG'in getirme (retrieval) katmanı derslerinde göreceksin.

Ne zaman derin öğrenme, ne zaman gradient boosting?

DurumTercih
Tablo verisi (müşteri, işlem, sipariş), binlerce–milyonlarca satırGradient boosting (LightGBM, XGBoost, HistGradientBoosting)
Görüntü, ses, serbest metinDerin öğrenme (çoğu zaman önceden eğitilmiş model)
Çok sayıda kategorik değer + metin + görüntü birlikteDerin öğrenme (embedding katmanları)
Az veri, açıklanabilirlik şart (kredi kararı)Lojistik regresyon ya da sığ ağaç modelleri
Gerçek işteBankalarda kredi ve churn skorlaması hâlâ büyük oranda gradient boosting ve lojistik regresyonla yapılır; açıklanabilirlik ve düzenleyici gereksinimler belirleyicidir. Derin öğrenme aynı kurumlarda belge okuma (OCR), çağrı merkezi ses analizi, dolandırıcılıkta işlem dizisi modelleri ve müşteri hizmetleri sohbet botlarında kullanılır.
Uzmanlaş

Başlatma ve normalizasyon

Ağırlıklar hepsi sıfırla başlatılırsa tüm nöronlar aynı şeyi öğrenir (simetri). ReLU ağlarında He başlatma (std = √(2/girdi)) yaygındır. Batch normalization ve layer normalization katman çıktılarını ölçekleyerek derin ağların kararlı eğitilmesini sağlar.

Gradyan kontrolü

Elle yazılan geri yayılımı test etmenin yolu sayısal türevdir: (L(w + ε) − L(w − ε)) / 2ε. Analitik ve sayısal gradyanın göreli farkı 1e-6 civarındaysa kod doğrudur. Meydan okuma alıştırmasında bunu yazacaksın.

Mülakat cümlesi

"Tablo verisinde önce gradient boosting ile güçlü bir temel çizgi kurarım; sinir ağını ancak veri yapısız ya da çok büyükse, ya da embedding gerekiyorsa denerim. Eğitim eğrisini izler, erken durdurma ve düzenlileştirme kullanır, girdileri Pipeline içinde ölçeklerim."

Alıştırmalara geç (21)