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

Cahit Barkin Ozer
10 min readMar 26, 2024

--

Bu bölümde tensörler üzerinde işlemler yaptıktan sonra Pytorch’ta iş akışını öğreniyoruz, veri oluşturup, temel bir model kurup onun sonuçlarını inceliyoruz.

For English:

Tensörleri Yeniden Şekillendirme, İstifleme ve Sıkıştırma (Reshaping Viewing, Stacking and Squeezing Tensors)

Şekil ile ilgili sorunlar Pytorch’ta en sık karşılaşılan sorunlardır ve yeniden şekillendirme genellikle bu amaçla yapılan eylemdir.

Görünüm, belirli bir şekle sahip bir tensörün görünümünü döndürür ancak orijinal tensörle aynı hafızaya sahip olmaya devam eder.

Şekillendirme ve görüntüleme (shaping and viewing) genellikle aynıdır ancak görüntülemede her zaman orijinal tensörle aynı belleğe sahip olunur.

İstifleme, birden fazla tensörün üst üste (vstack) veya yan yana (hstack) birleştirilmesidir.

Sıkıştırma, tensördeki 1 boyutun tamamını kaldırır. Gevşetmede (unsqueeze), hedef tensöre 1 boyut eklenir.

Permute, boyutların belirli bir şekilde değiştirildiği (yerdeğiştirdiği) bir girdinin görünümünü döndürür. Basit bir deyişle, öğeleri istediğiniz sırada yeniden düzenleyebilirsiniz.

import pytorch

x = torch.arange(1.,10.)
print(x) # tensor([1., 2., 3., 4., 5., 6., 7., 8., 9.])
print(x.shape) # torch.Size([9])

# Add an extra dimension
x_reshaped = x.reshape(1,9)

# Change the view
z = x.view(1,9)

# x_reshaped and z variables are same but z will point the same memory point as x
# So changing z changes x

x_stacked = torch.stack([x,x,x,x], dim=0) # Works
x_stacked = torch.stack([x,x,x,x], dim=1) # Works
x_stacked = torch.stack([x,x,x,x], dim=2) # NOT VALID


x = torch.zeros(2, 1, 2, 1, 2)
x.size() # torch.Size([2, 1, 2, 1, 2])

y = torch.squeeze(x)
y.size() # torch.Size([2, 2, 2])

y = torch.squeeze(x, 0)
y.size() # torch.Size([2, 1, 2, 1, 2])

y = torch.squeeze(x, 1)
y.size() # torch.Size([2, 2, 1, 2])

y = torch.squeeze(x, (1, 2, 3)) # torch.Size([2, 2, 2])

x = torch.tensor([1, 2, 3, 4])
torch.unsqueeze(x, 0) # tensor([[ 1, 2, 3, 4]])

torch.unsqueeze(x, 1)
# tensor([[1],[2],[3],[4]])


x = torch.randn(2, 3, 5)
x.size() # torch.Size([2, 3, 5])
torch.permute(x, (2, 0, 1)).size() # torch.Size([5, 2, 3])

Tensörlerden veri seçme

Pytorch ile indeksleme, NumPy ile indekslemeye benzer.

import torch

x = torch.arange(1,10).reshape(1,3,3)
print(x)
'''
tensor([[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]])
'''
print(x.shape) # torch.Size([1, 3, 3])

Yeni tensörümüzü indeksleyelim.

print(x[0])
'''
tensor([[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]])
'''

Ortanca diziyi, yani ilk boyutu indeksleyelim.

print(x[0][0]) # tensor([1, 2, 3])
print(x[0, 0]) # tensor([1, 2, 3])
print(x[:,0]) # tensor([1, 2, 3])
print(x[0,0,:])# tensor([1, 2, 3])
print(x[0][1][1]) # tensor(5)

Pytorch ve Numpy

Numpy bilimsel bir Python sayısal hesaplama kütüphanesi olduğu için Pytorch’un onunla etkileşim kurma işlevi vardır.

import numpy as np

array = np.arange(1.0, 8.0)
tensor = torch.from_numpy(array)
print(array) # [1. 2. 3. 4. 5. 6. 7.]
print(tensor) # tensor([1., 2., 3., 4., 5., 6., 7.], dtype=torch.float64)

Numpy’de varsayılan tip float64'tür ancak bunu değiştirebiliriz.

tensor32 = tensor.type(float32)

Pytorch Tekrarlanabilirliği (rastgeleden rastgele çıkarma)

Bir sinir ağı nasıl öğrenir?

Rastgele sayılarla başlayın -> tensör işlemleri -> rastgele sayıları güncelleyerek veriyi daha iyi temsil etmelerini sağlayın -> Tekrarlayın.

Tekrarlanabilirlik istediğinizde bu kadar rastgelelik istemezsiniz.

torch.rand(3,3)

Rastgeleliği azaltmak için rastgele tohum adı verilen bir kavram vardır. Bilgisayar biliminde gerçek rastgeleliği elde edemezsiniz. Elde ettiğiniz şey sözde rastgelelik veya üretilmiş rastgeleliktir. Örneğin bilgisayar bilimlerinde rastgele bir his elde etmek için, bazı algoritmaların tam yılı, ayı, günü, dakikası ve saniyesi tohum olarak alınır. Bazı bilim adamları en uygun rastgeleliği yaratmak için kozmik radyasyon vb. kullanmayı öneriyor, asıl noktayı anladınız.

import torch

RANDOM_SEED = 42
torch.manual_seed(RANDOM_SEED)
random_tensor_c = torch.rand(3,4)

torch.manual_seed(RANDOM_SEED)
random_tensor_d = torch.rand(3,4)

print(random_tensor_c == random_tensor_d)

Bir GPU’ya Erişme

CUDA sayesinde daha büyük tensör işlemlerini daha hızlı yapabilmek için GPU’ya ihtiyacınız var. Bir GPU’ya Google Collab’dan ücretsiz olarak erişebilir, GPU donanımını veya AWS ve Azure gibi bulut bilişim hizmetlerini satın alabilirsiniz.

Google Colab’da GPU’yu kontrol etmek için aşağıdaki komutu çalıştırın:

import torch
torch.cuda.is_available()

Yerel veya bulut cihazınıza CUDA’yı indirdikten sonra Nvidia GPU’yu kontrol etmek için aşağıdaki komutu çalıştırın:

nvidia-smi

Cihazdan bağımsız kodu ayarlamak için:

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

Cihaz sayısını saymak için:

torch.cuda.device_count()

Bir tensör oluşturduğumuzda, varsayılan olarak CPU’da çalışır ancak biz GPU’da çalıştırmak istiyoruz çünkü bu şekilde çok daha hızlıdır:

tensor = torch.tensor([1,2,3])

print(tensor.device) # cpu

device = "cuda" if torch.cuda.is_available() else "cpu"
tensor_on_gpu = tensor.to(device)
print(tensor_on_gpu.device) # cuda:0

Aşağıda Pytorch’taki en popüler 3. tür hata olan cihaz hatasına bir örnek verilmiştir.

tensor_on_gpu.numpy() # Can't convert cuda:0 device type tensor to numpy. 
#Use Tensor.cpu() to copy the tensor to host memory first.

GPU tensörünü NumPy sorunuyla düzeltmek için önce onu CPU’ya ayarlayabiliriz.:

tensor_on_gpu.cpu().gpu()

Tebrikler, bu Pytorch Temelleri’nin sonu. Daha fazla kontrol egzersizi ve ekstra müfredat için: https://www.learnpytorch.io/00_pytorch_fundamentals/#exercises

Pytorch İş Akışı

Veri hazırla ve onları tensörlere dönüştür -> Bir pretrained model eğit veya seç (Kayıp fonksiyonu ve optimize edici seç ve eğitim döngüsü oluştur)-> Veriyi model besle ve tahmin yaptır -> Modelin tahminini skorla -> Denemeler yaparak sonuçları geliştir -> Eğittiğin modeli kaydet.

nn, Pytorch’un sinir ağları için tüm yapı taşlarını içerir:

from torch import nn

Pytorch sürümünü kontrol edin:

torch.__version__

Veri Hazırlama ve Yükleme

Veriler neredeyse her şey olabilir; excel elektronik tabloları, resimler, videolar, ses, DNA, metin.

Temel olarak veri hazırlama, verileri sayısal bir gösterime sokmayı içerir ve yükleme, bu sayısal gösterimdeki kalıpları öğrenen bir model oluşturmak anlamına gelir.

Makine öğreniminde doğrusal regresyon adı verilen en temel modeli oluşturalım. Doğrusal regresyon formülü: y = ağırlık * X + sapma. Bu model veriler arasında düz bir çizgi çizer.

Doğrusal regresyon grafiği
weight = 0.7
bias = 0.3

start = 0
end = 1
step = 0.02
X = torch.arange(start, end, step).unsqueeze(dim=1)
y = weight * X + bias
print(len(X)) # 50
print(len(y)) # 50

Buradaki X büyüktür çünkü büyük harfler matrisleri ve tensörleri, küçük harfler ise vektörleri temsil ediyor.

Genelleştirme: Bir makine öğrenimi modelinin daha önce görmediği veriler üzerinde iyi performans gösterme yeteneğidir.

Veri kümeleri genellikle 3 bölümden oluşur; eğitim (~%60–80), test (~%10–20) ve doğrulama (~%10–20). Eğitim ve test kümeleri her zaman mevcut olsa da, doğrulama veri kümesi her zaman olmasa da sıklıkla mevcuttur.

Eğitim Verileri: Model bu verilerden öğrenir.

Doğrulama Verileri: Model bu verilere göre ayarlanır (final sınavından önce girdiğiniz deneme sınavı gibi).

Test Verisi: Model, öğrendiğini test etmek için bu veriler üzerinden değerlendirilir (dönem sonunda girdiğiniz final sınavı gibi).

Bir test ve eğitim bölümü oluşturun:

train_split = int(0.8 * len(X)) 
X_train, y_train = X[:train_split], y[:train_split]
X_test, y_test = X[train_split], y[train_split]

sklearn.train_test_split’i kullanırsanız bölme işlemine biraz rastgelelik katar.

def plot_predictions(train_data=X_train,
train_labels=y_train,
test_data=X_test,
test_labels=y_test,
predictions=None):
"""Plots training data, test data and compares predictions."""
plt.figure(figsize=(10, 7))

# Plot training data in blue
plt.scatter(train_data, train_labels, c="b", s=4, label="Training data")

# Plot test data in green
plt.scatter(test_data, test_labels, c="g", s=4, label="Testing data")

# Are there predictions?
if predictions is not None:
# Plot the predictions if they exist
plt.scatter(test_data, predictions, c="r", s=4, label="Predictions")

# Show the legend
plt.legend(prop={"size": 14})
There are no red dots because there are no predictions yet.

Pytorch’ta Model Oluşturma

Pytorch’taki hemen hemen her şey nn.Module’ü miras alır.

from torch import nn

class LinearRegressionModel(nn.Module):
def __init__(self):
super().__init__()
self.weights = nn.Parameter(torch.randn(1, requires_grad=True,dtype=torch.float))
self.bias = nn.Parameter(torch.randn(1, requires_grad=True,dtype=torch.float))
# Forward method to define the computation in the model
def forward(self, x: torch.Tensor) -> Torch.tensor:
return self.weights * x + self.bias # linear regression

Bu kod, rastgele ağırlıklar ve önyargı ile başlar, rastgele verilere bakar ve ağırlığı ve önyargıyı verilere mümkün olduğunca yakın ayarlar.

Model ağırlıklarını ve sapmalarını nasıl ayarlıyor?

Degrade iniş ve geri yayılma ile.

Aşağıda 3 temel sürdürülebilir cevap halinde yapılmış çok güzel animasyonlu anlatım videoları var. öğelerini izleyin. Videolar İngilizce ancak Türkçe altyazı bulunmaktadır.

Gradyan iniş (Gradient Descent) nedir?

Gradyan iniş, makine öğrenimi ve matematiksel optimizasyonda yaygın olarak kullanılan bir optimizasyon algoritmasıdır. Birincil amacı, fonksiyonun en dik iniş yönünde (yani eğimin negatifinde) yinelemeli olarak hareket ederek bir fonksiyonu en aza indirmektir.

  1. Başlatma: Optimize edilen fonksiyonun parametreleri için bir başlangıç tahminiyle başlayın. Bu parametreler genellikle teta (θ) olarak gösterilir.
  2. Gradyan Hesapla: Parametrelere göre amaç fonksiyonunun gradyanını hesaplayın. Eğim, en dik yükselişin yönünü verir. Yani fonksiyonun en hızlı arttığı yönü gösterir.
  3. Parametreleri Güncelle: Hedef fonksiyonunu en aza indirmek için parametreleri degradenin ters yönünde hareket ettirin. Bu, mevcut parametre değerlerinden eğimin bir kısmının (öğrenme oranı adı verilen bir faktörle ölçeklendirilmiş) çıkarılmasıyla yapılır. Parametreleri güncelleme formülü şöyledir: θ=θ−α∇J(θ) burada:
  • θ optimize edilen parametreleri temsil eder.
  • α (alfa), adım boyutunu belirleyen küçük bir pozitif skaler olan öğrenme hızıdır.
  • ∇J(θ), J(θ) amaç fonksiyonunun θ’ya göre gradyanını belirtir.

4. Tekrarla: Durdurma kriteri karşılanana kadar 2. ve 3. adımları tekrarlayın. Bu kriter, belirli bir düzeyde yakınsamanın elde edilmesi, maksimum yineleme sayısı veya çözülmekte olan soruna özgü diğer koşullar olabilir.

Algoritma, yavaş yavaş yerel bir minimuma (veya bazı durumlarda küresel minimuma) doğru yakınsayarak, parametreleri amaç fonksiyonunu en aza indirecek yönde yinelemeli olarak ayarlar. Öğrenme oranı, dikkatli bir şekilde seçilmesi gereken kritik bir hiperparametredir; çok küçük bir değer yakınsamanın yavaşlamasına yol açabilirken, çok büyük bir değer algoritmanın minimum değer etrafında sapmasına veya salınmasına neden olabilir.

Geri yayılım nasıl çalışır?

torch.nn: Hesaplamalı grafikler (sinir ağları) için tüm inşaa yapılarını içerir.

torch.nn.Parameter: Modelimizin hangi parametreleri denemesi ve öğrenmesi gerektiğidir; genellikle torch.nn’deki bir Pytorch katmanı bunu bizim için ayarlar.

torch.nn.Module: Tüm sinir ağı modülleri için temel sınıftır. Eğer onu alt sınıflandırırsanız (subclass), onu ezmelisiniz (override).

torch.optim: Burası Pytorch’taki optimizerlerin yaşadığı yerdir, gradyan inişine yardımcı olacaktır.

def forward(): Tüm nn.Module alt sınıfları forward()’ın üzerine yazmanızı gerektirir; bu yöntem ileri hesaplamada ne olacağını tanımlar.

torch.utils.data.Dataset: Verilerinizin anahtar(etiket) ve örnek(özellik) çiftleri arasındaki haritayı temsil eder. Resimler ve bunlarla ilişkili etiketler gibidir.

torch.utils.data.Dataloader: Bir torch Veri Kümesi üzerinde yinelenebilir bir Python oluşturur (verileriniz üzerinde yineleme yapmanızı sağlar).

Daha fazla bilgi için Pytorch kopya kağıdı sayfasına bakın: https://pytorch.org/tutorials/beginner/ptcheat.html

Her adımdaki Pytorch İş Akışı ve ortak kütüphaneler

Modelimizin içinde ne olduğunu kontrol edelim:

torch.manuel_seed(42)

model_0 = LinearRegressionModel()

print(list(model_0.parameters())
# [tensor([[-0.1234], ...]), # Weights tensor (example values shown)
# tensor([0.0])] # Biases tensor (example values shown)
print(model_0.state_dict()) # OrderedDict([('weights',tensor([0.3367])),('bias',tensor([0.1288]))])

Tahmin Yapmak

Modelimizin tahmin gücünü kontrol edelim.

torch.no_grad():

PyTorch tarafından sağlanan bir içerik yöneticisidir. Etkinleştirildiğinde PyTorch, bu bağlamda gerçekleşen işlemler için eğim hesaplamasını devre dışı bırakır. Bu, bu işlemlerde yer alan tensörler için hiçbir gradyanın hesaplanmayacağı anlamına gelir. Bu genellikle çıkarım veya doğrulama sırasında, degradelere ihtiyacınız olmadığında ve geri geçişler için ara değerleri saklamayarak hesaplamaları hızlandırmak istediğinizde kullanılır.

torch.inference_mode():

Bellek ayak izini azaltmak ve potansiyel olarak ek optimizasyonlara olanak sağlamak gibi çıkarım amaçları doğrultusunda hesaplamayı optimize etmek üzere tasarlanmıştır. Etkinleştirildiğinde, çıkarım performansını artırmak için belirli işlemlerin davranışını değiştirebilir. torch.no_grad()’ın aksine, torch.inference_mode() yalnızca degrade hesaplamasını etkilemez, aynı zamanda işlemlerin nasıl yürütüldüğünü de etkileyebilir. Bu işlev esas olarak PyTorch modellerini çıkarım hızının ve kaynak kullanımının kritik olduğu üretim ortamlarına dağıtırken kullanılır.

with torch.inference_mode():
y_preds1 = model_0(X_test)

with torch.no_grad():
y_preds2 = model_0(X_test)

print(y_preds1)
print(y_preds2)
print(y_test)
# Both results are far away from close but not hallicunations (too irrelevant)
plot_predictions(predictions=y_preds1)
plot_predictions(predictions=y_preds2)

Eğitim, verilerdeki ilişkiyi/örüntüyü özetleyen bir model oluşturmayı amaçlamaktadır.

Modelin ne kadar iyi özetlediğinin başarısını ölçmenin bir yolu kayıp fonksiyonunu kullanmaktır. Kayıp fonksiyonu farklı alanlarda maliyet fonksiyonu veya kriteri olarak adlandırılabilir.

Tüm kayıp fonksiyonları torch.nn’de mevcuttur: https://pytorch.org/docs/stable/nn.html#loss-functions

Optimize edici, kayıp fonksiyonunun sonuçlarını kontrol eder ve modelin parametrelerini ayarlar.

Mutlak Hataların Ortalaması (MAE —Mean Absolute Error)

MAE popüler bir kayıp fonksiyonudur. MAE, tahmin ile gerçek değer arasındaki farkın ortalamasını alır. Bu şekilde kullanılabilir:

MAE_loss =  torch.nn.L1Loss

# stochastic gradient descend as optimizer (lr= learning rate)
optimizer = torch.optim.SGD(params=model.parameters(), lr=0.001)

Hangi kayıp fonksiyonunu ve optimize ediciyi kullanmanız gerektiği soruna özeldir. Örneğin doğrusal bir regresyon modeli için kayıp fonksiyonu olarak L1Loss ve optimizer olarak SGD yeterli olacaktır. Bir sınıflandırma problemi için, kayıp fonksiyonu olarak nn.BCELoss() (İkili Çapraz Entropi Kaybı/ Binary Cross Entropy Loss) ve optimize edici olarak torch.optim.RMSProp() yeterli olacaktır.

Pytorch’ta bir eğitim ve test döngüsü oluşturma

  1. Veriler üstünde dönün.
  2. Yayılmalara ilişkin tahminler yapmak için ileri geçiş(forward pass) (verilerin forward() işleviyle modelimizin parametreleri üzerinden geçmesi).
  3. Kaybı hesaplayın.
  4. Optimize edici sıfır gradyan.
  5. Kayıp geriye doğru / geriye yayılım (kayıptaki parametrelerin her birinin gradyanlarını hesaplamak için ağda geriye doğru hareket edin).
  6. Optimize edici/gradyan düşüşü (kaybı denemek ve iyileştirmek amacıyla modelin parametrelerini ayarlamak için optimize ediciyi kullanın).

Dönem: Bir dönem, verilerdeki bir döngüdür.

Hiperparametre: Hiperparametreler, modelin en iyi performansı göstermesi için insanlar tarafından ince ayar yapılan değerlerdir. Kesin olarak doğru değerler yoktur, bu yüzden insanlar bunlara ince ayar yapar.

epochs = 1

for epoch in range(epochs):
model_0.train()
y_pred = model_0(X_train)
loss= loss_fn(y_pred, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
model_0.eval() # turns of gradient tracking

Modelimizi çalıştıralım

epochs = 100

for epoch in range(epochs):
model_0.train()
y_pred = model_0(X_train)
loss = loss_fn(y_pred, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
model_0.eval()

with torch.inference_mode():
y_preds_new = model_0(X_test)

plot_predictions(predictions= y_preds_new)

Yukarıdaki şekilde tahminlerin giderek yaklaştığını görüyoruz. 100 epoch daha çalıştırırsak model yaklaşacaktır ama bu sefer modelin o veriyi ezberleme ihtimali var o yüzden %95'ten fazla yaklaşmamayı tercih ediyoruz.

Son adım, geliştirmeyi tamamladıktan sonra modeli kaydetmektir.

Modeli Kaydetme

Pytorch’ta modelleri kaydetmek ve yüklemek için farklı teknikler vardır. state_dict’i veya modelin tamamını kaydedip yükleyebilirsiniz. state_dict, her katmanı kendi parametre tensörüne eşleyen bir Python sözlük nesnesidir. Modelin state_dict dosyasında yalnızca öğrenilebilir parametrelere (evrişimli katmanlar, doğrusal katmanlar vb.) ve kayıtlı arabelleklere (batchnorm’un Running_mean’ı) sahip katmanların girişleri bulunduğunu unutmayın.

  • torch.save(): Bir Pytorch nesnesini Python’un pkl formatında kaydetmenizi sağlar.
  • torch.load(): Kaydedilmiş bir Pytorch nesnesini yüklemenizi sağlar.
  • torch.nn.Module.load_state_dict(): Bu, bir modelin kayıtlı durum sözlüğünün (state_dict) yüklenmesine olanak tanır.
from pathlib import Path

MODEL_PATH = Paths("models")
MODEL_PATH.mkdir(parents=True, exist_ok=True)

MODEL_NAME="01_pytorch_workflow_model_0.pth"
MODEL_SAVE_PATH= MODEL_PATH/ MODEL_NAME

# saving the model
torch.save(obj=model_0.state_dict(), f=MODEL_SAVE_PATH)
# loading the model
loaded_model = LinearRegressionModel().load_state_dict(torch.load(f=MODEL_SAVE_PATH))

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
Cahit Barkin Ozer

Written by Cahit Barkin Ozer

Üretken YZ başta olmak üzere teknoloji alanındaki yenilikleri öğrenip sizlerle paylaşıyorum. Youtube Kanalım: https://www.youtube.com/@cbarkinozer

No responses yet