PyTorch Ustalık Serisi — Bölüm 3

Cahit Barkin Ozer
8 min readApr 23, 2024

PyTorch ile sinir ağları oluşturmak ve eğitmek için kapsamlı bir kılavuz olması için başladığım serinin 3. bölümü.

For English:

Sinir Ağı Sınıflandırması

Sınıflandırma, şeyleri benzerliklerine ve yakınlıklarına göre gruplar ve çeşitli bireyler arasında var olabilecek niteliklerin birliğini ifade eder.

Örneğin, e-posta spam sınıflandırması ikili bir sınıflandırma sorunudur. Görüntüler içerdikleri şeye göre sınıflandırılabilir, bu çok sınıflı sınıflandırmadır. Belgeler etiket eklemek için sınıflandırılabilir; bu, çok etiketli bir sınıflandırmadır.

Neleri ele alacağız:

  • Sinir Ağı Sınıflandırma modeli mimarisi.
  • Bir sınıflandırma modelinin girdi ve çıktı şekilleri.
  • Görüntülemek, eğitmek ve tahmin etmek için özel veriler oluşturma.
  • Modellemedeki adımlar: bir model oluşturma, bir kayıp fonksiyonu ve optimize edici ayarlama, bir eğitim döngüsü oluşturma ve bir modeli değerlendirme.
  • Modelleri kaydetme ve yükleme.
  • Doğrusal olmamanın gücünden yararlanmak.
  • Farklı sınıflandırma değerlendirme yöntemleri.
Sınıflandırma nasıl çalışır?
Bir görüntünün özellikleri ve şekli
İkili ve çok sınıflı sınıflandırma için hiper parametreler

Sınıflandırma verileri hazırlayalım.

import sklearn
from sklearn.datasets import make_circles

n_samples = 1000

X,y = make_circles(n_samples, noise = 0.03, random_state = 42)

print(len(X)) # 1000
print(len(y)) # 1000

print(X[:5])
print(y[:5])

# Daire verilerinden veri çerçevesi oluşturur

import pandas as pd
circles = pd.DataFrame({"X1":X[:,0],"X2":X[:,1],"label":y})
circles.head(10)
import matplotlib.pyplot as plt
plt.scatter(x=X[:,0],y=X[:,1],c=y,cmap=plt.cm.RdY1Bu)

Verileri tensörlere dönüştürelim ve eğitim ve test şeklinde bölelim

import torch
X = torch.from_numpy(X).type(torch.float)
y = torch.from_numpy(y).type(torch.float)

torch.manual_seed(42)

from sklearn.model_selection import train_test_split # eğitim ve testi rastgele böler
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

Mavi ve kırmızı noktaları sınıflandıran bir model oluşturalım. Bir GPU kuracağız, bir model oluşturacağız, kayıp fonksiyonunu ve optimize ediciyi tanımlayacağız ve bir eğitim ve test döngüsü oluşturacağız.

import torch
from torch import nn

device = "cuda" if torch.cuda.is_available() else "cpu"

Artık cihazdan bağımsız kodu ayarladık, şu şekilde bir model oluşturalım:

  1. ‘nn.Module’ alt sınıfını extend edelim(neredeyse tüm modellerde olduğu gibi).
  2. Verilerimizin şekillerini işleyebilen 2 ‘nn.Linear()’ katmanı oluşturalım.
  3. İleri geçişi özetleyen bir ‘forward()’ yöntemi tanımlayalım.
  4. Model sınıfımızın bir örneğini oluşturup ve onu hedef ‘cihaza’ gönderelim.
class CircleModelV0(nn.Module):
def __init__(self):
super().__init__()
self.layer_1 = nn.Linear(in_features=2, out_features=5) # 2 özelliği alır ve 5 özelliğe yükseltir
self.layer_2 = nn.Linear(in_features=5, out_features=5) # önceki katmandan 5 özellik alır ve tek bir özelliğin çıktısını alır (y ile aynı şekilde)

def forward(self, x):
return self.layer_2(self.layer_1(x)) # x -> layer_1 -> layer_2 -> output

model_0 = CircleModelV0().to(device)

Aşağıdaki sitedeki sınıflandırma simülasyonundan bir sinir ağının nasıl çalıştığını inceleyebilirsiniz:

https://playground.tensorflow.org/

Yukarıdaki CircleModelV0 gibi daha basit yaklaşımlar kullanacaksanız aşağıdaki yöntemi de kullanabilirsiniz:

model_0 = nn.Sequential(
nn.Linear(in_features=2,out_features=5),
nn.Linear(in_features=5,out_features=1)
).to(device)

Veya ikisini de şu şekilde karıştırabilirsiniz:

class CircleModelV0(nn.Module):
def __init__(self):
super().__init__()
self.two_linear_layers = nn.Sequential(
nn.Linear(in_features=2,out_features=5),
nn.Linear(in_features=5,out_features=1)
)

def forward(self, x):
return two_linear_layers(x)

model_0 = CircleModelV0().to(device)

Tahminlerde bulunun:

untrained_preds = model_0(X_test.to(device))

Sınıflandırma için ikili çapraz entropi veya kategorik çapraz entropi kullanabilirsiniz. Kayıp fonksiyonu için ‘torch.nn.BECWithLogitsLoss()’ kullanacağız.

Doğruluğu hesaplayalım. Doğruluk istediğimiz şey yani mutlak gerçektir. Doğruluk şu formülle hesaplanır:

Accuracy = True Positive / (True Positive + True Negative) * 100

Accuracy = Doğruluk, True Positive = Gerçek Doğru, True Negative = Gerçek Yanlış

def accuracy(y_true, y_pred):
correct = torch.eq(y_true, y_pred).sum().item()
acc = (correct/len(y_pred)) * 100
return acc

Modelin çıktılarına logit denir. Logit’ler, herhangi bir aktivasyon fonksiyonunu uygulamadan önce bir model tarafından oluşturulan ham, normalleştirilmemiş tahminlerdir. Bu logitleri bir tür aktivasyon fonksiyonuna (örneğin ikili sınıflandırma için sigmoid ve çok sınıflı sınıflandırma için softmax) aktararak tahmin olasılıklarına dönüştürebiliriz.

Logit’ler -> tahmin olasılıkları -> tahmin etiketleri

Daha sonra modelimizin tahmin olasılıklarını yuvarlayarak veya argmax() değerini alarak tahmin etiketlerine dönüştürebiliriz.

model_0.evaluation()
with torch.inference_mode():
y_logits = model_0(X_test.to(device))[:5]

y_pred_probs = torch.sigmoid(y_logits)

Tahmin olasılık değerlerimiz için, bunlar üzerinde aralık stili yuvarlama yapmamız gerekir:

y_pred_probs ≥0.5, y=1 (class 1)

y_pred_probs <0.5, y=0 (class 0)

# Tahmin edilen etiketleri bulun
y_preds = torch.round(y_pred_probs)

# Tam olarak (logitler -> tercih probları -> tercih etiketleri)
y_pred_labels = torch.round(torch.sigmoid(model_0(X_test.to(device))[:5]))

# Eşitliği kontrol edin
print(torch.eq(y_preds.squeeze(), y_pred_labels.squeeze()))

# Ekstra boyutlardan kurtulun
y_preds.squeeze()

Bir eğitim ve test döngüsü oluşturma

torch.manuel_seed(42)
torch.cuda.manuel_seed(42)

epochs = 100

X_train, y_train = X_train.to(device), y_train.to(device)
X_test, y_train = X_test.to(device), y_Test.to(device)

# Eğitim ve test döngüsü
for epoch in range(epochs):
model_0.train()

# Doğrudan geçiş
y_logits = model_0(X_train).squeeze()
y_pred = torch.round(torch.sigmoid(y_logits))

# Kayıp/Doğruluğu Hesapla
loss = loss_fn(y_logits, y_train)
acc = accuracy_fn(y_true=y_train, y_pred=y_pred)

optimizer.zero_grad()
loss.backward() # geri yayılım
optimizer.step() # gradyan alçalma

model_0.eval()
with torch.inference_mode():
test_logits = model_0(X_test).squeeze()
test_pred = torch.round(torch.sigmoid(test_logits))

test_loss = loss_fn(test_logits, y_test)
test_acc = accuracy_fn(y_true= y_test, y_pred=test_pred)

if epoch % 10 == 0:
print(f"Epoch: {epoch}, Loss: {loss:.5f}, Accuracy: {acc.2f}%, Test Loss:{test_loss.5f}, Test Accuracy: {test_acc.2f}%")

Yukarıdakini çalıştırıp da Accuracy değerlerini gördüğümüzde modelimizin iyi öğrenmediğini görüyoruz. Sebebini daha iyi anlamak için tahminleri görselleştirelim. Plot_decision_boundary() yöntemini içe aktaralım.

import requests
from pathlib import Path

if Path("helper_functions.py").is_file():
pass
else:
request = requests.get("https://raw.githubusercontent.com/mrdbourke/pytorch-deep-learning/")
with open("helper_functions.py","wb") as f:
f.write(request.content)

from helper_functions import plot_predictions, plot_decision_boundary

plt.figure(figsize=(12,6))
plt.subplot(1,2,1)
plt.train("Train")
plot_decision_boundary(model_0, X_train, y_train)
plt.subplot(1,2,2)
plt.title("Test")
plot_decision_boundary(model_0,X_test,y_test)

Bir modelin performansı nasıl artırılır?

  • Daha fazla katman eklemek, modele verilerdeki kalıplar hakkında daha fazla bilgi edinme şansı verir.
  • Daha fazla gizli birim eklemek (genellikle bu tarz basit düzeyde bir problem için 5'ten 10'a kadar gizli katmana sayısını arttırmak denenebilir).
  • Daha uzun süre eğitmek.
  • Etkinleştirme işlevlerini değiştirme.
  • Öğrenme oranını değiştirmek (oran çok küçükse öğrenmez, öğrenme çok büyükse gradyanlar patlar).
  • Verileri ön işleme gibi yöntemlerle (NaN değerlerinin silinmesi vb.) değiştirebilirsiniz.
Hiperparametreleri ayarlayarak ve katman ekleyerek model performansının iyileştirilmesine ilişkin örnek

Deney Yapma Yöntemi: Hangi değişikliğin sonuç üzerinde en olumlu etkiye sahip olduğunu anlayabilmek için bu değişiklikleri adım adım yapmalısınız.

Bu değişikliklerden sonra hala iyi bir kümeleme oluşturamıyoruz.

Veri setini önceki regresyonumuzla değiştiriyoruz ve modelimizin öğrenme konusunda bir sorunu olmadığını, sorunun veri setinde olduğunu görüyoruz.

Sorun, modelimizin doğrusal olmayan çizgilere ihtiyacımız olduğunu doğrusal çizgilerle tahmin etmesiydi.

Doğrusal olmayan bir model oluşturma

from torch import nn

class CircleModelV2(nn.Module):
def __init__(self):
super().__init__()
self.layer_1 = nn.Linear(in_features=2,out_features=10)
self.layer_2 = nn.Linear(in_features=10,out_features=10)
self.layer_3 = nn.Linear(in_features=10,out_features=1)
self.relu = nn.Relu()
self.sigmoid = nn.Sigmoid()

def forward(self, x):
return self.layer_3(self.relu(self.layer_2(self.relu(self.layer_1(x)))))

model_2 = CircleModelV2().to(device)

loss_fn = nn.BCEWithLogitsLoss()
optimizer = torch.optim.SGD.(model_3.parameters(), lr=0.1)

torch.manual_seed(42)
torch.cuda.manual_seed(42)

X_train, y_train = X_train.to(device), y_train.to(device)
X_test, y_test = X_test.to(device), y_test.to(device)

epochs = 1000

for epoch in range(epochs):
model_2.train()
y_logits = model_3(X_train).squeeze()
y_pred = torch.round(torch.sigmoid(y_logits))

loss = loss_fn(y_logits, y_train)
acc = accuracy_fn(y_true=y_train, y_pred = y_pred)

optimizer = zero_grad()
loss.backward()

optimizer.step()

model_2.eval()
with torch.inference_mode():
test_logits = model_2(X_Tests).squeeze()
test_pred = torch.round(torch.sigmoid(test_logits))

test_loss = loss_fn(test_logits, y_test)
test_acc = accuracy_fn(y_true = y_test, y_pred = test_pred)

if epoch % 100 == 0:
print(f"Epoch: {epoch}, Loss: {loss:.5f}, Accuracy: {acc.2f}%, Test Loss:{test_loss.5f}, Test Accuracy: {test_acc.2f}%")

Öğrenme yeteneğini artırmak için daha fazla parametre daha iyidir ancak eğitim ve çıkarım hızını düşürür ve ayrıca daha fazla hesaplama gücü gerektirir.

Modelimizin doğruluğunu doğrusal olmayan bir aktivasyon fonksiyonuyla değerlendirelim:

model_2.eval()
with torch.inference_mode():
y_preds = torch.round(torch.sigmoid(model_2(X_test))).squeeze()

plt.figure(figsize = (12,6))
plt.subplot(1, 2, 1)
plt.title("Train")
plot_decision_boundry(model_3, X_train, y_train)
plt.subplot(1, 2, 1)
plt.title("Test")
plot_decision_boundary(model_3, X_test, y_test)

Sol alt köşede küçük bir tutarsızlık olmasına rağmen çıktının kalitesi oldukça doğrudur.

Çok Sınıflı Sınıflandırma

Çok sınıflı sınıflandırma, 2'den fazla sınıfı sınıflandırdığınız zamandır. Çok sınıflı sınıflandırmada, çıktı katmanı şekli, gizli katman aktivasyonu, çıktı aktivasyonu, kayıp fonksiyonu ve optimize edici gibi bazı hiper parametreler ikili sınıflandırmadan farklıdır.

import torch
import matplotlib.pyplot as plt
import sklearn.datasets import make_blobs # toy dataset
import sklearn.model_selection import train_test_split

NUM_CLASSES = 4
NUM_FEATURES = 2
RANDOM_SEED = 42

X_blob, y_blob = make_blobs(n_samples = 1000,
n_features = NUM_FEATURES,
centers = NUM_CLASSES,
cluster_std = 1.5,
random_state= RANDOM_SEED)

X_blob = torch.from_numpy(X_blob).type(torch.float)
y_blob = torch.from_numpy(y_blob).type(torch.LongTensor)

X_blob_train, X_blob_test, y_blob_train, y_blob_test = train_test_split(X_blob, y_blob,test_size=0.2,random_state=RANDOM_SEED)

plt.figure(figsize=(10,7))
plt.scatter(X_blob[:,0], X_blob[:,1], c=y_blob, cmap=plt.cm.RdYlBu)

Çok sınıflı sınıflandırma modelini oluşturma

device = "cuda" if torch.cuda.is_available() else "cpu"

class BlobModel(nn.Module):
def __init__(self, input_features, output_features, hidden_units=8):
super().__init__()
self.linear_layer_stack = nn.Sequential(
nn.Linear(in_features=input_features, out_features=hidden_units),
nn.ReLU(),
nn.Linear(in_features=hidden_units, out_features=hidden_units),
nn.ReLU(),
nn.Linear(in_features=hidden_units, out_features=output_features)
)

def forward(self, x):
return self.linear_layer_stack(x)

model_3 = BlobModel(input_features=2, output_features=4, hidden_units=8).to(device)

Çok sınıflı bir örnek için kayıp fonksiyonu ve optimize edici oluşturma

loss_fn = nn.CrossEntropyLoss()

optimizer = torch.optim.SGD(params=model_3.parameters(), lr=0.1)

Sonuçları tahmin etmek

Modelimizi değerlendirmek, test etmek ve eğitmek için modelimizin çıktılarını tahmin olasılıklarına ve ardından tahmin etiketlerine dönüştürmemiz gerekir.

model_3.eval()
witch torch.inference_mode():
y_preds = model_3(X_blob_test.to(device))

y_pred_probs = torch.softmax(y_logits, dim=1)
# Convert our model's prediction probabilities to prediction label
y_preds = torch.argmax(y_pred_probs, dim=1)

Eğitim ve test döngüsü oluşturma

torch.manual_seed(42)
torch.cuda.manual_seed(42)
epochs=100

X_blob_train, y_blob_train = X_blob_train.to(device), y_blob_train.to(device)
X_blob_test, y_blob_test = X_blob_test.to(device), y_blob_test.to(device)

for epoch in range(epochs):
model_3.train()

y_logits = model_3(X_blob_train)
y_pred = torch.softmax(y_logits, dim=1).argmax(dim=1)

loss = loss_fn(y_logits, y_blob_train)
acc = accuracy_fn(y_true= y_blob_train, y_pred=y_pred)

optimizer.zero_grade()
loss.backward()
optimizer.step()

model_3.eval()

with torch.inference_mode():
test_logits = model_3(X_blob_test)
test_preds = torch.softmax(test_logits, dim=1).argmax(dim=1)

test_loss = loss_fn(test_logits, y_blob_test)
test_acc = accuracy_fn(y_true=y_blob_test, y_pred = test_preds)


if epoch % 100 == 0:
print(f"Epoch: {epoch}, Loss: {loss:.5f}, Accuracy: {acc.2f}%, Test Loss:{test_loss.5f}, Test Accuracy: {test_acc.2f}%")

Tahminlerin Değerlendirilmesi

model_3.eval()

with torch.inference_mode():
y_logits = model_3(X_blob_test)

y_pred_probs = torch.softmax(y_logits, dim=1)

plt.figure(figsize=(12,6))
plt.subplot(1,2,1)
plt.title("Train")
plot_decision_boundary(model_3, X_blob_train, y_blob_train)
plt.subplot(1,2,1)
plt.title("Test")
plot_decision_boundary(model_3, X_blob_test, y_blob_test)

YSA’mızdaki ReLU katmanlarını kaldırırsak, bu örnekte de işe yarayacaktır çünkü öğelerimiz doğrusal olarak ayrılabilir, ancak öğelerimiz doğrusal olarak ayrılamaz olsaydı (pizzadaki malzemeler gibi) işe yaramazdı.

Sınıflandırma Metrikleri

Sınıflandırma problemlerinde modelin değerlendirilmesi için çeşitli metrikler mevcuttur. En yaygın kullanılan metriklerden bazıları şunlardır:

1. Doğruluk (Accuracy)

Doğruluk metriği, modelin toplam tahminlerinin ne kadarının doğru olduğunu ölçer. Formülü tp+tn / (tp+tn+fp+fn) şeklindedir.

import torchmetrics
accuracy = torchmetrics.Accuracy()

2. Hassasiyet (Precision)

Hassasiyet metriği, modelin pozitif olarak tahmin ettiği örneklerin arasından kaçının gerçekten pozitif olduğunu ölçer. Formülü tp / (tp+fp) şeklindedir.

import torchmetrics
precision = torchmetrics.Precision()

3. Geri Çağırma (Recall)

Geri çağırma metriği, modelin gerçekte pozitif olan örneklerin ne kadarını doğru olarak tahmin ettiğini ölçer. Formülü tp / (tp+fn) şeklindedir.

import torchmetrics
recall = torchmetrics.Recall()

4. F1-puanı (F1-score)

F1-puanı metriği, hassasiyet ve geri çağırmanın harmonik ortalamasıdır. Formülü 2 * (hassasiyet * geri çağırma) / (hassasiyet + geri çağırma) şeklindedir.

import torchmetrics
f1_score = torchmetrics.F1Score()

5. Karmaşıklık Matrisi (Confusion matrix)

Karmaşıklık matrisi metriği, modelin tahminleri ile gerçek etiketler arasındaki ilişkiyi görselleştirir. torchmetrics.ConfusionMatrix() kullanılarak oluşturulabilir.

import torchmetrics
confusion_matrix = torchmetrics.ConfusionMatrix()

Modelimizin doğruluğunu hesaplayalım:

!pip install torchmetrics

from torchmetrics import Accuracy

torchmetric_accuracy = Accuracy().to(device)

torchmetric_accuracy(y_preds, y_blob_test)

Kaynak

[1] freeCodeCamp.org, (6 Ekim 2022), PyTorch for Deep Learning & Machine Learning — Full Course:

[https://www.youtube.com/watch?v=V_xro1bcAuA&t]

--

--

Cahit Barkin Ozer

Daha fazla şey öğrenmek ve daha iyi olmak isteyen bir yazılım mühendisi.