Tensörler
PyTorch’un ana yapı taşı Tensörlerdir. Basit katsayılardan n
boyutlu dizilere kadar her şeyi temsil edebilirler. PyTorch ile Tensörler üzerinde 100’den fazla işlem yapılabilir, aynı zamanda türevler rastgele birçok matematiksel işlemi uyguladıktan sonra hesaplanabilir. Dokümanın bu kısım, tensörlerin temellerini ve tensörler üzerindeki türev hesaplamalarını kapsayacaktır.
Tensör oluşturma
Mevcut Veriden Tensör Oluşturma
Python iç içe diziler oluşturabilir ve bunları tensör oluşturmak için kullanabilir. dtype
özelliğini kullanarak tensördeki sayıların veri türünü belirtebilir veya PyTorch’un bunları otomatik olarak çıkarmasına izin verebilirsiniz.
import torch
scaler = 10
array = [1,2]
matrix = [[3.1,4.0,5.0],[6.5,-71,8.02]]
s_tensor = torch.tensor(scaler, dtype=torch.int32)
a_tensor = torch.tensor(array, dtype=torch.float32)
m_tensor = torch.tensor(matrix)
print(f"Ölçekleyiciler \n{s_tensor}\n")
print(f"1D dizisi \n{a_tensor}\n")
print(f"2D matris \n{m_tensor}\n")
Çıktı
Ölçekleyiciler
10
1D dizisi
tensor([1., 2.])
2D matris
tensor([[ 3.1000, 4.0000, 5.0000],
[ 6.5000, -71.0000, 8.0200]])
Sabit değerler kullanma
Bir tensör, gerekli tensör şekli belirtilerek oluşturulabilir. Bu durumda veri yapısı, rastgele veya kullanıcı tarafından tanımlanabilen sabit bir değerle doldurulabilir. Tensörün şekli, birden çok bağımsız değişken, bir demet, veya bir liste olarak belirtilir. Aşağıda bazı örnekler verilmiştir:
import torch
rand_uni = torch.rand((2,3)) # tekdüze rastgele değerlere sahip matris
rand_nor = torch.randn(2,3) # normal dağılımdan rastgele değerlere
# sahip matris
all_ones = torch.ones(4) # Bir ile dolu 1D dizisi
all_zeros = torch.zeros([2,3,2]) # 3 sıralı sıfır tensörü
all_six = torch.full((2,2), 6) # tüm değerleri 6'ya eşit olan matris
regular = torch.arange(1,2,0.2) # 0,2 aralıklı [1,2) aralığında 1B değer
# dizisi
print(f"Tekdüze dağılımdan rastgele \n{rand_uni}\n")
print(f"Normal dağılımdan rastgele \n{rand_nor}\n")
print(f"Birler \n{all_ones}\n")
print(f"Sıfırlar \n{all_zeros}\n")
print(f"Sabit \n{all_six}\n")
print(f"Bir dizi değer \n{regular}\n")
Çıktı
Tekdüze dağılımdan rastgele
tensor([[0.5713, 0.6914, 0.8229],
[0.6467, 0.3670, 0.2972]])
Normal dağılımdan rastgele
tensor([[-0.2732, -1.3975, 0.3833],
[-0.3211, 0.2996, 0.0519]])
Birler
tensor([1., 1., 1., 1.])
Sıfırlar
tensor([[[0., 0.],
[0., 0.],
[0., 0.]],
[[0., 0.],
[0., 0.],
[0., 0.]]])
Sabit
tensor([[6, 6],
[6, 6]])
Bir dizi değer
tensor([1.0000, 1.2000, 1.4000, 1.6000, 1.8000])
Diğer tensörleri kullanma
Mevcut tensörlerden de yeni tensörler yaratılabilir. Bu işlem, değerleri, şekli ve veri türleri dahil olmak üzere diğer tensör özelliklerinin kullanılmasına izin verir.
import torch
rand_nor = torch.randn(2,3, dtype=torch.float32) # rastgele tensör
all_sevens = torch.zeros_like(rand_nor) # aynı şekil ve veri türü
all_ones = torch.ones_like(rand_nor, dtype=torch.float64) # aynı şekil, farklı
# veri türü
exact_copy = rand_nor.clone().detach() # aynı tensör
print(f"Orijinal tensör \n{rand_nor}\n")
print(f"Aynı şekil ve veri türü \n{all_sevens}\n")
print(f"Aynı şekil, farklı veri türü \n{all_ones}\n")
print(f"Tam kopya \n{exact_copy}\n")
Çıktı
Orijinal tensör
tensor([[ 0.5975, 1.1559, 0.5189],
[-1.1280, -0.0791, 1.2801]])
Aynı şekil ve veri türü
tensor([[0., 0., 0.],
[0., 0., 0.]])
Aynı şekil, farklı veri türü
tensor([[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
Tam kopya
tensor([[ 0.5975, 1.1559, 0.5189],
[-1.1280, -0.0791, 1.2801]])
Tensör Manipülasyonu
Tensörlere erişme (dilimleme)
Bir tensöre ya da dilimlerine, numpy benzeri indeksleme ile kolayca erişilebilir.
import torch
const_ten = torch.tensor([[1,2,3], [4,5,6]], dtype=torch.float32)
single_element = const_ten[1,2]
first_row = const_ten[0,:]
second_column = const_ten[:, 1]
sub_matrix = const_ten[0:2,1:3]
print(f"Tek eleman \n{single_element}\n")
print(f"İlk sıra \n{first_row}\n")
print(f"İkinci sütun \n{second_column}\n")
print(f"Alt matris \n{sub_matrix}\n")
Çıktı
Tek eleman
6.0
İlk sıra
tensor([1., 2., 3.])
İkinci sütun
tensor([2., 5.])
Alt matris
tensor([[2., 3.],
[5., 6.]])
Tensörleri yeniden şekillendirmek
Tensörler, reshape
ve reshape_as
işlevleri kullanılarak kolayca yeniden şekillendirilebilir.
Önemli: reshape(_as)
işlemleri yeni bir tensör döndürür, ancak yeni tensör orijinal tensörle aynı verileri kullanır. Orijinal tensörün verilerinin yenisine kopyalandığından emin olmak için clone
işlevini kullanabilirsiniz.
import torch
all_ones = torch.zeros(2,4)
diff_shape = all_ones.reshape((2,4)) # Şekli bir liste ile belirtin
diff_shape_1 = all_ones.reshape((1,2,4))
diff_shape_2 = all_ones.reshape(-1, 2) # Bir boyuta -1 koymak, PyTorch'a geri kalan değerlere bakarak
# boyutu otomatik olarak çıkarmasını söyler
rand_t = torch.empty((2, 2, 2))
diff_shape_3 = all_ones.reshape_as(rand_t) #Başka bir tensörün şekliyle
#eşleşmeyi kullanabilirsiniz
new_tensor = all_ones.clone().detach().reshape((2,4))
#Başka bir tensörün şekliyle eşleşmeyi kullanabilirsiniz.
#clone ile verinin kopyalanmasını sağlayabiliriz.
print("Şekil: (2,3)")
print(all_ones)
print("\nŞekil: (3,2)")
print(diff_shape)
print("\nŞekil: (1,2,3)")
print(diff_shape_1)
print("\nŞekil: (4,2)")
print(diff_shape_2)
print("\nŞekil: (2,2,2)")
print(diff_shape_3)
print("\nYeni tensör:")
print(new_tensor)
Çıktı
Şekil: (2,3)
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.]])
Şekil: (3,2)
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.]])
Şekil: (1,2,3)
tensor([[[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
Şekil: (4,2)
tensor([[0., 0.],
[0., 0.],
[0., 0.],
[0., 0.]])
Şekil: (2,2,2)
tensor([[[0., 0.],
[0., 0.]],
[[0., 0.],
[0., 0.]]])
Yeni tensör:
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.]])
Tensörleri birleştirme
Tensörler herhangi bir eksende (boyut üzerinden) birleştirilebilir. Birleştirilmiş tensör, yeni bir tensör olarak döndürülür.
import torch
all_ones = torch.ones(2,3)
all_zeros = torch.zeros_like(all_ones) # all_ones ile aynı şekil
con_hor = torch.cat([all_ones, all_zeros], dim=1) # yatay
con_ver = torch.cat([all_ones, all_zeros], dim=0) # dikey
print(f"Yatay birleştirme \n{con_hor}\n")
print(f"Dikey birleştirme \n{con_ver}\n")
Çıktı
Yatay birleştirme
tensor([[1., 1., 1., 0., 0., 0.],
[1., 1., 1., 0., 0., 0.]])
Dikey birleştirme
tensor([[1., 1., 1.],
[1., 1., 1.],
[0., 0., 0.],
[0., 0., 0.]])
Matematiksel işlemler
Tensörler üzerinde yapılabilecek birçok matematiksel işlem vardır. Tam bir listeye bu linkten erişebilirsiniz.
import torch
all_ones = torch.ones(3,2, dtype=torch.float32)
all_twos = torch.full((2,3),2, dtype=torch.float32)
all_threes = torch.full((3,2),3, dtype=torch.float32)
scaler_arith = all_ones + 4
tensor_arith = all_ones - all_threes
scaler_mul = all_ones * 2
elem_mul = all_ones * all_threes
mat_mul = all_ones.matmul(all_twos)
print(f"Bir tensöre ölçekleyici ekleme \n{scaler_arith}\n")
print(f"İki tensör eklemek \n{tensor_arith}\n")
print(f"Bir tensörün bir ölçekleyici ile çarpılması \n{scaler_mul}\n")
print(f"Element-bilge çarpma \n{elem_mul}\n")
print(f"Matris çarpımı \n{mat_mul}\n")
Çıktı
Bir tensöre ölçekleyici ekleme
tensor([[5., 5.],
[5., 5.],
[5., 5.]])
İki tensör eklemek
tensor([[-2., -2.],
[-2., -2.],
[-2., -2.]])
Bir tensörün bir ölçekleyici ile çarpılması
tensor([[2., 2.],
[2., 2.],
[2., 2.]])
Element-bilge çarpma
tensor([[3., 3.],
[3., 3.],
[3., 3.]])
Matris çarpımı
tensor([[4., 4., 4.],
[4., 4., 4.],
[4., 4., 4.]])
GPU üzerindeki tensörler
PyTorch’daki diğer veri yapıları gibi tensörler de GPU ve CPU arasında transfer edilebilir. doğrudan CPU ve GPU’da yaratılabilirler. Farklı cihazlar (CPU ya da GPU) üzerinde bulunan tensörler üzerinde matematiksel işlemler gerçekleştirilemez.
import torch
gpu_0 = torch.device('cuda') #
cpu_device = torch.device('cpu')
t1 = torch.tensor([1,2,3], device=gpu_0)
print(f"t1 on GPU 0: \n{t1}\n")
t2 = torch.tensor([1,2,3])
print(f"t2 on CPU: \n{t2}\n")
t2 = t2.to(gpu_0)
print(f"t2 on GPU 0: \n{t2}\n")
t3 = t2 + t1
t3 = t3.to(cpu_device)
print(f"t3 on GPU 0: \n{t3}\n")
Çıktı
t1 on GPU 0:
tensor([1, 2, 3], device='cuda:0')
t2 on CPU:
tensor([1, 2, 3])
t2 on GPU 0:
tensor([1, 2, 3], device='cuda:0')
t3 on CPU:
tensor([2, 4, 6])
Veri üzerinde olan ve olmayan operasyonlar
Genel olarak, operasyonlar veri üzerinde yapılmaz. Bu, bir işlemin işlenenleri değiştirmeyeceği ve yeni bir veri yapısı kullanacağı ve döndüreceği anlamına gelir. Bununla birlikte, _
karakteriyle biten işlevler veri üzerinde çalışır. Örneğin, t3 = t1.mul (t2)
, eleman bazında t1
ve t2
tensörlerini çarpacak ve sonucu t3
içinde saklayacaktır. Ancak, t1.mul_(t2)
, t1
ve t2
yi eleman bazında çarpacak ve sonucu t1
de saklayacaktır.
Türev Hesaplama
PyTorch’un en önemli özelliklerinden biri, torch.autograd
paketidir. Tensörlerin ve ölçekleyicilerin türevlerinin kolaylıkla hesaplanmasını sağlar. Bu, geri yayılma (ing., back-propagation) sürecini yönlendirdiği için makine öğrenimi süreçlerindeki ardışık düzenleri oluşturmak için çok kullanışlıdır.
Türev hesabı için gerekenler
Bir tensörün türevinin hesaplanabilir olması için, tensöre tanımlı bir türeve ihtiyaç duyduğumuzu tensör yaratma işlemi sırasında, ya da daha sonra bir fonksiyon çağırarak belirtmemiz gerekir.
import torch
t0 = torch.ones(3, requires_grad=True)
t1 = torch.zeros(3)
print(f"t0 \n{t0}")
print(f"t1 \n{t1}\n")
t1.requires_grad_(True)
print(f"t1 \n{t1}")
print("t1'de manuel olarak etkinleştirilen türev hesaplaması \n")
t1.requires_grad_(False)
print(f"t1 \n{t1}")
print("Manuel olarak devre dışı bıraktı ")
Çıktı
t0
tensor([1., 1., 1.], requires_grad=True)
t1
tensor([0., 0., 0.])
t1
tensor([0., 0., 0.], requires_grad=True)
t1'de manuel olarak etkinleştirilen türev hesaplaması
t1
tensor([0., 0., 0.])
Manuel olarak devre dışı bıraktı
Türevleri hesaplama
Bir t1
tensörünün requires_grad
seçeneğini True
olarak ayarlandığında, başka bir t2
tensörünün t1
e göre türevini hesaplayabiliriz. Bunu, t2
üzerinde backward ()
işlevini çağırarak yapabiliriz.
import torch
t1 = torch.tensor(1, dtype=torch.float32, requires_grad=True)
t2 = t1*t1 # t2, t1 cinsinden bir fonksiyondur
# dt2/dt1 = 2*t1
t2.backward()
print(f"t1 = {t1}")
print(f"t1'e göre t2'nin türevi = {t1.grad}")
Çıktı
t1 = 1.0
t1'e göre t2'nin gradyanı = 2.0
Daha derin işlemler
Tensörlerin türevleri zincir kuralı kullanılarak hesaplanır, bu da onların istenilen derinlikteki fonksiyonlar için hesaplanabileceği anlamına gelir. Bu türevler işlemin herhangi bir ara adımı için de hesaplanabilirler. Bununla birlikte, tek bir türevden daha fazlası hesaplanacaksa, backward()
fonksiyonuna retain_graph
seçeneği eklenmelidir.
import torch
t1 = torch.tensor(1, dtype=torch.float32, requires_grad=True)
t2 = t1*t1-5 # dt2/dt1 = 2*t1
t3 = t2*2+3 # dt3/dt2 = 2
t4 = t3**4 # dt4/g3 = 4*t3^3
print(f"t1 = {t1}, t2 = {t2}, t3 = {t3}, t4 = {t4}")
t2.backward(retain_graph=True) # türevi tekrar hesaplayabilmek
# için "retain_graph = True" belirtiriz
print(f"t1'ye göre t2'nin gradyanı = {t1.grad}")
# dt2/dt1 = 2 * t1
t1.grad.data.zero_() # bu türevi 0'a eşitleyecek
t3.backward(retain_graph=True)
print(f"t1'e göre t3'ün gradyanı = {t1.grad}")
# dt3/dt1 = dt3/dt2 * dt2/dt1 = 2 * 2 * t1
t1.grad.data.zero_() # bu türevi 0'a eşitleyecek
t4.backward()
print(f"t1'e göre t4 gradyanı = {t1.grad}")
# dt4/dt1 = dt4/dt3 * dt3/dt2 * dt2/dt1 = 4 * t3^3 * 2 * 2 * t1
t1.grad.data.zero_() # bu türevi 0'a eşitleyecek
Çıktı
t1 = 1.0, t2 = -4.0, t3 = -5.0, t4 = 625.0
t1'ye göre t2'nin gradyanı = 2.0
t1'e göre t3'ün gradyanı = 4.0
t1'e göre t4 gradyanı = -2000.0
no_grad kullanarak tensörleri güncelleme
Genel olarak, makine öğrenimi ardışık düzenlerinde, bir tensörün gradyanı, bu tensörün değerini güncellemek için kullanılır. Tensörü gradyanını kullanarak güncellerken, güncelleme prosedürünün autograd
paketi tarafından izlenmediğinden emin olmalıyız. Başka bir deyişle, güncelleme işlemini sürecin ileriye doğru yayılmasının bir parçası olarak işaretlememeliyiz. Bunu, tüm gradyan izlemeyi durduran torch.no_grad()
işlevini kullanarak yapıyoruz.
import torch
t1 = torch.tensor(1, dtype=torch.float32, requires_grad=True)
t2 = t1*t1-5 # dt2/dt1 = 2*t1
t2.backward() # t1'ye göre t3'ün gradyanını hesaplayın
print(f"t1 = {t1}")
print(f"t1'e göre t2'nin gradyanı = {t1.grad.data}\n")
with torch.no_grad(): # gradyanlar hesaplanırken bu bloktaki
t1-=t1.grad.data # tensör işlemleri izlenmez
print(f"güncellemeden sonra t1 = {t1}")
t1.grad.data.zero_() # bu degradeyi 0'a sıfırlayacak
Çıktı
t1 = 1.0
t1'e göre t2'nin gradyanı = 2.0
güncellemeden sonra t1 = -1.0