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).
| Aktivasyon | Formül | Nerede? |
|---|---|---|
| ReLU | max(0, z) | Gizli katmanların varsayılanı; hızlı, gradyanı sönmez |
| Sigmoid | 1 / (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 |
| Softmax | ezi / Σ 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.
dZ2 = (p − y) / ndW2 = A1ᵀ @ dZ2,db2 = dZ2.sum(axis=0)dA1 = dZ2 @ W2ᵀ, ReLU'nun türevi:dZ1 = dA1 · (Z1 > 0)dW1 = Xᵀ @ dZ1,db1 = dZ1.sum(axis=0)- Her parametreyi
param −= η · gradile 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üğün | Olası neden | Ne 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ükseliyor | Aşırı öğrenme (overfitting) | Erken durdurma, dropout, L2, daha çok veri |
| İkisi de yüksek ve düz | Model çok küçük | Nöron/katman ekle, daha uzun eğit |
Aşırı öğrenmeye karşı: düzenlileştirme, dropout, erken durdurma
- L2 düzenlileştirme (weight decay): kayba
λ·Σw²ekler, büyük ağırlıkları cezalandırır. sklearn'dealpha. - Dropout: eğitimde her adımda nöronların rastgele bir kısmını (p oranında) sıfırlar; ağ tek bir nörona yaslanamaz. "Ters dropout" (inverted dropout) kalanları
1/(1 − p)ile ölçekler ki tahmin anında hiçbir şey yapmak gerekmesin. Tahminde dropout kapalıdır. - Erken durdurma (early stopping): doğrulama kaybı
sabir(patience) epoch boyunca iyileşmezse dur ve en iyi epoch'un ağırlıklarına dön.
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)))
pip 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
| Mimari | Fikir | Tipik kullanım |
|---|---|---|
| CNN (evrişimli ağ) | Küçük filtreler görüntü üzerinde kayar; komşu piksellerden yerel desen öğrenir | Görüntü sınıflandırma, belge tarama, kalite kontrol |
| RNN / LSTM | Diziyi adım adım okur, gizli durum taşır | Eski nesil dil ve zaman serisi modelleri |
| Transformer | Dikkat (attention) ile her öğe diğer tüm öğelere bakar; paralel eğitilir | Dil 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?
| Durum | Tercih |
|---|---|
| Tablo verisi (müşteri, işlem, sipariş), binlerce–milyonlarca satır | Gradient boosting (LightGBM, XGBoost, HistGradientBoosting) |
| Görüntü, ses, serbest metin | Derin öğrenme (çoğu zaman önceden eğitilmiş model) |
| Çok sayıda kategorik değer + metin + görüntü birlikte | Derin öğrenme (embedding katmanları) |
| Az veri, açıklanabilirlik şart (kredi kararı) | Lojistik regresyon ya da sığ ağaç modelleri |
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."