PyTorch ile Kodlama

Ayla Bilgin
7 min readMay 7, 2021

--

Bu yazıda adım adım PyTorch kütüphanesini nasıl kullanacağımızdan ve neler yapabileceğimizden bahsedeceğiz. Bir önceki yazımda PyTorch’un özelliklerinden ve ne olduğundan bahsetmiştik.

[1]

İlk olarak PyTorch kütüphanelerinden bahsedelim. Temelinde iki kütüphane bulunmaktadır. Bunlar ‘torch.utils.data.DataLoader’ ve ‘torch.utils.data.Dataset’tir. Kodumuza eklerken aşağıdaki gibi yaparız.

from torch.utils.data import DataLoader
from torch.utils.data import Dataset

Burada yazacağımız kod için aşağıdaki kütüphaneleri ekleyerek başlayalım.

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda, Compose
import matplotlib.pyplot as plt

PyTorch’un içerisinde veri setlerini içeren kendilerine özgü kitaplıklar vardır.

  • TorchText
  • TorchVision
  • TorchAudio

Biz TorchVision’u kullanacağız. Birçok veri setinin içerisinden FashionMNIST veri setini modelimize yükleyelim.


training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor(),
)

test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=ToTensor(),
)

Buraya kadar olan kısım çalıştırıldığında veri setini indirmiş oluyoruz ve test-eğitim olarak ayırıyoruz. Şimdi 64'lük bir boyut tanımlıyoruz. Bu boyut verideki her elemanın 64 özelliğini ve etiketini içeren bir grup döndürecektir.

batch_size = 64

train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

for X, y in test_dataloader:
print("Shape of X [N, C, H, W]: ", X.shape)
print("Shape of y: ", y.shape, y.dtype)
break

Yine çıktı olarak datanın boyutlarını alıyoruz. Model oluştururken CPU veya GPU seçeneklerinden hangisini kullandığımızı öğrenmemiz gerekiyor. GPU üzerinde yapılırsa haliyle işlemler daha hızlı gerçekleştirilecektir. __init__ ile katmanları yaptık.


device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
nn.ReLU()
)

def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits

model = NeuralNetwork().to(device)
print(model)
Çıktı:
Using cuda device
NeuralNetwork(
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear_relu_stack): Sequential(
(0): Linear(in_features=784, out_features=512, bias=True)
(1): ReLU()
(2): Linear(in_features=512, out_features=512, bias=True)
(3): ReLU()
(4): Linear(in_features=512, out_features=10, bias=True)
(5): ReLU()
)
)

Çalıştırdıktan sonra cudanın kullanıldığını yazan bir yazı çıkıyor ve diğer çıkan yazılar sinir ağının yapısını tanımlıyor. Kayıp veriler için ise aşağıdaki kod parçaları kullanılır.

loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)

Model Kaydetme:

Modelimizi kaydetmek için .save komutu kullanılır. .save içerisine hangi isimle kaydedeciğimizi yazarız.

torch.save(model.state_dict(), "model.pth")
print("Saved PyTorch Model State to model.pth")
Çıktı:
Saved PyTorch Model State to model.pth

Kodları yazdığımız dosya içerisinde sıkıştırılmış bir dosya olarak kaydettiğimiz modeli görüntüleyebiliriz.

Model Yükleme:

Diyelim ki oluşturduğumuz modeli kod içerisinde açmak istiyoruz. Bu işlemi de aşağıdaki kodlarla gerçekleştirebiliriz.

model = NeuralNetwork()
model.load_state_dict(torch.load("model.pth"))

Yüklediğimiz model ile tahmin sürecini yürütebiliriz. Görüldüğü üzere önce sınıfları oluşturduk. Ardından test datasından aldığımız veriler ile tahmin sürecini yürüttük.

classes = [
"T-shirt/top",
"Trouser",
"Pullover",
"Dress",
"Coat",
"Sandal",
"Shirt",
"Sneaker",
"Bag",
"Ankle boot",
]

model.eval()
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
pred = model(x)
predicted, actual = classes[pred[0].argmax(0)], classes[y]
print(f'Predicted: "{predicted}", Actual: "{actual}"')
Çıktı:
Predicted: "Sandal", Actual: "Ankle boot"

Tensörler:

Tensör, dizilere benzeyen özel bir veri yapısıdır. PyTorch’da modelin parametrelerini kodlamak için kullanılır. Kütüphaneleri ekleyerek başlayabiliriz.

import torch
import numpy as np

Tensörleri direkt olarak verilerden oluşturmak istersek aşağıdaki şekildeki gibi yapabiliriz.

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

NumPy dizilerinden yapmak istersek ise:

np_array = np.array(data)
x_np = torch.from_numpy(np_array)

Başka bir tensörden alarak oluşturmak istersek:

x_ones = torch.ones_like(x_data)
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float)
print(f"Random Tensor: \n {x_rand} \n")
Çıktı:
Ones Tensor:
tensor([[1, 1],
[1, 1]])
Random Tensor:
tensor([[0.4136, 0.8226],
[0.6868, 0.5999]])

şeklinde yazılabilir. İlk kodda 1'lerden oluşan bir matris oluştururken, ikinci kodda random oluşturuldu.

Şimdi de shape ile boyutları belirleyerek bir tensör oluşturalım. İlki random olarak, ikincisi 1'lerden oluşacak şekilde ve üçüncüsü ise 0'lardan oluşacak şekilde kodlandı.

shape = (5,4,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")
Çıktı:
Random Tensor:
tensor([[0.0642, 0.8239, 0.0335, 0.2809],
[0.9316, 0.2513, 0.6903, 0.7242],
[0.1532, 0.4187, 0.2566, 0.1544],
[0.0016, 0.2316, 0.4015, 0.7307],
[0.9685, 0.2284, 0.9701, 0.3894]])
Ones Tensor:
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
Zeros Tensor:
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])

Komutlarla, tensörün boyutunu, veri tipini ve nerede depolandığını aşağıdaki gibi öğrenebiliriz.

tensor = torch.rand(4,5)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
Çıktı:
Shape of tensor: torch.Size([4, 5])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu

Tensörlerle işlemler de gerçekleştirebilmekteyiz. Yine CPU veya GPU üzerinde bu işlemleri gerçekleştirebilirsiniz. Önce tensörler CPU üzerinde varsayılan olarak oluşturulur. Bu tensörleri GPU’ya taşımamız gerekir. Bunu da aşağıdaki gibi yaparız. ’to’ komutu bunu sağlar.

if torch.cuda.is_available():
tensor = tensor.to('cuda')

Tensörü bölmek istersek aynı NumPy üzerinde yaptığımız gibi elde edebiliriz. 1'lerden oluşturduğumuz tensörü bu şekilde yazdırabiliriz.

tensor = torch.ones(4, 4)
print('First row: ',tensor[0])
print('First column: ', tensor[:, 0])
print('Last column:', tensor[..., -1])
tensor[:,1] = 0
print(tensor)
Çıktı:
First row: tensor([1., 1., 1., 1.])
First column: tensor([1., 1., 1., 1.])
Last column: tensor([1., 1., 1., 1.])
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])

Birleştirmek için ise ‘.cat’ komutu kullanılır.

t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)
Çıktı:
tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])

Tensör ile NumPy’ın birbirine benzediğini söylemiştik. Bu durumda aralarında geçiş yapabilmek de mümkün oluyor. Aşağıdaki gibi bunu yapabiliriz. Tensörde yaptığımız herhangi bir işlem veya değişiklik NumPy dizisine de yansır.

t = torch.ones(7,2)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
Çıktı:
t: tensor([[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.]])
n: [[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]]

Peki önce NumPy dizisi oluşturup ardından tensör oluştursak. Bunu da benzer şekilde yapıyoruz ve yine benzer şekilde NumPy dizisinde yapılan bir değişiklik tensör üzerine yansır.

n = np.ones(5)
print(f”n: {n}”)
t = torch.from_numpy(n)
print(f”t: {t}”)
Çıktı:
n: [1. 1. 1. 1. 1.]
t: tensor([1., 1., 1., 1., 1.], dtype=torch.float64)

Veri Seti Yükleme:

Aslında en başta da bir veri seti yüklemeyi göstermiştik. Ama burda da ayrı olarak bahsedelim. Fashiın-MNIST veri setini yükleyeceğiz. İçerisinde 60.000 eğitim verisi, 10.000 adet de test verisi olarak makale görüntülerini bulundurur. Her veri 28x28 gri tonlamalı görüntü ve 10 sınıftan oluşan etiketler içerir.

‘root’ komutu test ve eğitim verilerinin depolandığı kısmı belirtir. ‘train’ komutu verinin eğitim veya test verisi olduğunu belirtir. ‘download=True’ kısmında veri mevcut değilse internetten indirilmesini sağlar. ‘transform’ ve ‘target_transform’ komutları ise etiket dönüşümlerini belirtir.

import torch
from torch.utils.data import Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt


training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor()
)

test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=ToTensor()
)

Verileri görselleştirmek için ‘.matplotlib’ kütüphanesinden faydalanılır. Bunu aşağıdaki gibi sağlayabiliriz.

labels_map = {
0: "T-Shirt",
1: "Trouser",
2: "Pullover",
3: "Dress",
4: "Coat",
5: "Sandal",
6: "Shirt",
7: "Sneaker",
8: "Bag",
9: "Ankle Boot",
}
figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3
for i in range(1, cols * rows + 1):
sample_idx = torch.randint(len(training_data), size=(1,)).item()
img, label = training_data[sample_idx]
figure.add_subplot(rows, cols, i)
plt.title(labels_map[label])
plt.axis("off")
plt.imshow(img.squeeze(), cmap="gray")
plt.show()

Bu kodun görsel olarak çıktısı da aşağıdaki gibi olur. Bu işlem random olarak yani rastgele gerçekleştirilmiştir.

Dönüşümler:

Bazı durumlarda veriler elimizde işlenmiş şekilde bulunmaz. Verileri istediğimiz forma getirmek için dönüşümleri (transform) kullanıyoruz. Yukarda da söylediğimiz iki parametre üzerinden dönüşümleri sağlarız. Bunlar:

  • transform
  • target_transform

Aşağıda FashionMNIST veri setinin nasıl dönüştürüldüğü verilmiştir.

from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda

ds = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor(),
target_transform=Lambda(lambda y: torch.zeros(10, dtype=torch.float).scatter_(0, torch.tensor(y), value=1))
)

Sinir Ağı Oluşturma:

Sinir ağları katmanlardan oluşur. Şimdi FashionMNIST için bir sinir ağı oluşturalım. Kütüphanelerimizi ekleyerek başlayabiliriz.

import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print('Using {} device'.format(device))
Çıktı:
Using cuda device

Sinir ağını alt sınıflara ayırarak tanımlıyoruz. Bunları ‘__init__’ komutları ile yapıyoruz. Yapısını görmek için bir örnek oluşturduk.

class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
nn.ReLU()
)

def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
model = NeuralNetwork().to(device)
print(model)
Çıktı:
NeuralNetwork(
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear_relu_stack): Sequential(
(0): Linear(in_features=784, out_features=512, bias=True)
(1): ReLU()
(2): Linear(in_features=512, out_features=512, bias=True)
(3): ReLU()
(4): Linear(in_features=512, out_features=10, bias=True)
(5): ReLU()
)
)

Modeli kullanmak için verileri koda gireriz. Aşağıda bunun bir örneği verilmiştir.

X = torch.rand(1, 28, 28, device=device)
logits = model(X)
pred_probab = nn.Softmax(dim=1)(logits)
y_pred = pred_probab.argmax(1)
print(f"Predicted class: {y_pred}")
Çıktı:
Predicted class: tensor([9], device='cuda:0')

Modelin katmanlarını ayırmak için ise aşağıdaki kodları kullanırız. 28x28 boyutunda 3 görüntüden oluşan bir parça aldık.

input_image = torch.rand(3,28,28)
print(input_image.size())
Çıktı:
torch.Size([3, 28, 28])

Her bir görüntüyü 784 piksel değerinden oluşan bitişik bir diziye dönüştürmek için ‘nn.Flatten()’ komutunu kullanıyoruz.

flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())
Çıktı:
torch.Size([3, 784])

Lineer tabaka ile depolanmış verileri kullanarak dönüşüm aşağıdaki gibi gerçekleşir.

layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())
Çıktı:
torch.Size([3, 20])

Bu yazıyı burada sonlandırıyoruz. Sonraki yazılarda görüşmek üzere…

--

--