3.3. Doğrusal Regresyonunun Kısa Uygulaması¶ Open the notebook in SageMaker Studio Lab
Son birkaç yıldır derin öğrenmeye olan geniş ve yoğun ilgi, gradyan tabanlı öğrenme algoritmalarını uygulamanın tekrarlayan iş yükünü otomatikleştirmek için şirketler, akademisyenler ve amatör geliştiricilere çeşitli olgun açık kaynak çerçeveleri geliştirmeleri için ilham verdi. Section 3.2 içinde, biz sadece (i) veri depolama ve doğrusal cebir için tensörlere; ve (ii) gradyanları hesaplamak için otomatik türev almaya güvendik. Pratikte, veri yineleyiciler, kayıp işlevleri, optimize ediciler ve sinir ağı katmanları çok yaygın olduğu için, modern kütüphaneler bu bileşenleri bizim için de uygular.
Bu bölümde, derin öğrenme çerçevelerinin üst düzey API’lerini kullanarak Section 3.2 içindeki doğrusal regresyon modelini kısaca nasıl uygulayacağınızı göstereceğiz.
3.3.1. Veri Kümesini Oluşturma¶
Başlamak için, şuradaki aynı veri kümesini oluşturacağız: Section 3.2.
from d2l import mxnet as d2l
from mxnet import autograd, gluon, np, npx
npx.set_np()
true_w = np.array([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
import numpy as np
import tensorflow as tf
from d2l import tensorflow as d2l
true_w = tf.constant([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
3.3.2. Veri Kümesini Okuma¶
Kendi yineleyicimizi döndürmek yerine, verileri okumak için bir
çerçevedeki mevcut API’yi çağırabiliriz. features
(öznitelikleri
tutan değişken) ve labels
’i (etiketleri tutan değişken) bağımsız
değişken olarak iletiriz ve bir veri yineleyici nesnesi başlatırken
batch_size
(grup boyutu) belirtiriz. Ayrıca, mantıksal veri tipi
(boolean) değeri is_train
, veri yineleyici nesnesinin her bir
dönemdeki (epoch) verileri karıştırmasını isteyip istemediğimizi
gösterir (veri kümesinden geçer).
def load_array(data_arrays, batch_size, is_train=True): #@save
"""Bir Gluon veri yineleyici oluşturun."""
dataset = gluon.data.ArrayDataset(*data_arrays)
return gluon.data.DataLoader(dataset, batch_size, shuffle=is_train)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
def load_array(data_arrays, batch_size, is_train=True): #@save
"""Bir PyTorch veri yineleyici oluşturun."""
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
def load_array(data_arrays, batch_size, is_train=True): #@save
"""Bir TensorFlow veri yineleyici oluşturun."""
dataset = tf.data.Dataset.from_tensor_slices(data_arrays)
if is_train:
dataset = dataset.shuffle(buffer_size=1000)
dataset = dataset.batch(batch_size)
return dataset
batch_size = 10
data_iter = load_array((features, labels), batch_size)
Şimdi, data_iter
i, data_iter
işlevini
Section 3.2 içinde çağırdığımız şekilde
kullanabiliriz. Çalıştığını doğrulamak için, örneklerin ilk minigrubunu
okuyabilir ve yazdırabiliriz. Section 3.2 içindeki
ile karşılaştırıldığında, burada bir Python yineleyici oluşturmak için
iter
kullanıyoruz ve yineleyiciden ilk öğeyi elde etmek için
next
’i kullanıyoruz.
next(iter(data_iter))
[array([[ 0.62162733, 0.3694969 ],
[-1.4381213 , -0.98106235],
[-0.21166277, -0.224368 ],
[-0.5176199 , 0.07872018],
[-0.6308071 , 0.53733146],
[-2.1471155 , -0.6573725 ],
[-1.4797637 , -2.1362736 ],
[-0.43897745, 1.0051775 ],
[ 0.8858836 , 0.20588513],
[ 0.4123393 , -0.2761083 ]]),
array([[ 4.1955047 ],
[ 4.6493993 ],
[ 4.5605335 ],
[ 2.8932831 ],
[ 1.0948168 ],
[ 2.157684 ],
[ 8.517494 ],
[-0.08255066],
[ 5.2522955 ],
[ 5.971049 ]])]
next(iter(data_iter))
[tensor([[-0.7894, -0.4236],
[-0.1704, -1.6098],
[-1.3614, 1.1222],
[-0.3556, 0.7254],
[-1.0136, -0.1632],
[ 1.0528, 1.5194],
[ 2.1396, -2.2619],
[-0.2321, -0.7793],
[ 1.4198, -1.2292],
[ 1.3337, -1.1340]]),
tensor([[ 4.0635],
[ 9.3159],
[-2.3210],
[ 1.0009],
[ 2.7228],
[ 1.1503],
[16.1817],
[ 6.3618],
[11.2123],
[10.7317]])]
next(iter(data_iter))
(<tf.Tensor: shape=(10, 2), dtype=float32, numpy=
array([[-0.11027498, -0.36329874],
[-0.45996955, -0.5091588 ],
[ 0.6684956 , -0.2517032 ],
[ 1.1874642 , 0.6640608 ],
[-0.6636225 , -0.10674559],
[ 0.6567242 , -1.1437957 ],
[-1.0788041 , -0.7574406 ],
[ 0.35696584, -0.21034554],
[-0.5199094 , -0.08901736],
[ 0.80963707, -1.0619783 ]], dtype=float32)>,
<tf.Tensor: shape=(10, 1), dtype=float32, numpy=
array([[5.198727 ],
[5.0174804],
[6.389131 ],
[4.3275056],
[3.246684 ],
[9.408877 ],
[4.6261787],
[5.6210465],
[3.456949 ],
[9.439413 ]], dtype=float32)>)
3.3.3. Modeli Tanımlama¶
Doğrusal regresyonu Section 3.2 içinde sıfırdan uyguladığımızda, model parametrelerimizi açık bir şekilde tanımladık ve temel doğrusal cebir işlemlerini kullanarak çıktı üretmek için hesaplamalarımızı kodladık. Bunu nasıl yapacağınızı bilmelisiniz. Ancak modelleriniz daha karmaşık hale geldiğinde ve bunu neredeyse her gün yapmanız gerektiğinde, bu yardım için memnun olacaksınız. Durum, kendi blogunuzu sıfırdan kodlamaya benzer. Bunu bir veya iki kez yapmak ödüllendirici ve öğreticidir, ancak bir bloga her ihtiyaç duyduğunuzda tekerleği yeniden icat etmek için bir ay harcarsanız kötü bir web geliştiricisi olursunuz.
Standart işlemler için, uygulamaya (kodlamaya) odaklanmak yerine
özellikle modeli oluşturmak için kullanılan katmanlara odaklanmamızı
sağlayan bir çerçevenin önceden tanımlanmış katmanlarını kullanabiliriz.
Önce, Sequential
(ardışık, sıralı) sınıfının bir örneğini ifade
edecek net
(ağ) model değişkenini tanımlayacağız. Sequential
sınıfı, birbirine zincirlenecek birkaç katman için bir kapsayıcı
(container) tanımlar. Girdi verileri verildiğinde, Sequential
bir
örnek, bunu birinci katmandan geçirir, ardından onun çıktısını ikinci
katmanın girdisi olarak geçirir ve böyle devam eder. Aşağıdaki örnekte,
modelimiz yalnızca bir katmandan oluşuyor, bu nedenle gerçekten
Sequential
örneğe ihtiyacımız yok. Ancak, gelecekteki modellerimizin
neredeyse tamamı birden fazla katman içereceği için, sizi en standart iş
akışına alıştırmak için yine de kullanacağız.
Tek katmanlı bir ağın mimarisini şurada gösterildiği gibi hatırlayın: Fig. 3.1.2. Katmanın tamamen bağlı olduğu söylenir, çünkü girdilerinin her biri, bir matris-vektör çarpımı yoluyla çıktılarının her birine bağlanır.
Gluon’da tamamen bağlı katman Dense
(Yoğun) sınıfında tanımlanır.
Sadece tek bir skaler çıktı üretmek istediğimiz için, bu sayıyı 1 olarak
ayarladık.
Kolaylık sağlamak için Gluon’un her katman için girdi şeklini
belirlememizi gerektirmediğini belirtmek gerekir. Yani burada, Gluon’a
bu doğrusal katmana kaç girdi girdiğini söylememize gerek yok.
Modelimizden ilk veri geçirmeye çalıştığımızda, örneğin, daha sonra
net(X)
’i çalıştırdığımızda, Gluon otomatik olarak her katmana girdi
sayısını çıkaracaktır. Bunun nasıl çalıştığını daha sonra daha ayrıntılı
olarak anlatacağız.
# `nn` sinir ağları için kısaltmadır
from mxnet.gluon import nn
net = nn.Sequential()
net.add(nn.Dense(1))
PyTorch’ta, tam bağlantılı katman Linear
sınıfında tanımlanır.
nn.Linear
’e iki bağımsız değişken aktardığımıza dikkat edin.
Birincisi, 2 olan girdi öznitelik boyutunu belirtir ve ikincisi, tek bir
skaler olan ve dolayısıyla 1 olan çıktı öznitelik boyutudur.
# `nn` sinir ağları için kısaltmadır
from torch import nn
net = nn.Sequential(nn.Linear(2, 1))
Keras’ta tamamen bağlı katman Dense
(Yoğun) sınıfında tanımlanır.
Sadece tek bir skaler çıktı üretmek istediğimiz için, bu sayıyı 1 olarak
ayarladık.
Kolaylık sağlamak için Gluon’un her katman için girdi şeklini
belirlememizi gerektirmediğini belirtmek gerekir. Yani burada, Keras’a
bu doğrusal katmana kaç girdi girdiğini söylememize gerek yok.
Modelimizden ilk veri geçirmeye çalıştığımızda, örneğin, daha sonra
net(X)
’i çalıştırdığımızda, Keras otomatik olarak her katmana girdi
sayısını çıkaracaktır. Bunun nasıl çalıştığını daha sonra daha ayrıntılı
olarak anlatacağız.
# `keras` TensorFlow'un üst-seviye API'sidir
net = tf.keras.Sequential()
net.add(tf.keras.layers.Dense(1))
3.3.4. Model Parametrelerini İlkleme¶
net
’i kullanmadan önce, doğrusal regresyon modelindeki ağırlıklar ve
ek girdi gibi model parametrelerini ilkletmemiz gerekir. Derin öğrenme
çerçeveleri genellikle parametreleri ilklemek için önceden tanımlanmış
bir yola sahiptir. Burada, her ağırlık parametresinin, ortalama 0 ve
standart sapma 0.01 ile normal bir dağılımdan rastgele örneklenmesi
gerektiğini belirtiyoruz. Ek girdi parametresi sıfır olarak
ilklenecektir.
MXNet’ten initializer
modülünü içe aktaracağız. Bu modül, model
parametresi ilkletme için çeşitli yöntemler sağlar. Gluon, init
’i
initializer
paketine erişmek için bir kısayol (kısaltma) olarak
kullanılabilir hale getirir. Ağırlığın nasıl ilkleneceğini sadece
init.Normal(sigma=0.01)
’i çağırarak belirtiyoruz. Ek girdi
parametreleri sıfır olarak ilklenecektir.
from mxnet import init
net.initialize(init.Normal(sigma=0.01))
Yukarıdaki kod basit görünebilir, ancak burada tuhaf bir şeylerin olduğunu fark etmelisiniz. Gluon, girdinin kaç boyuta sahip olacağını henüz bilmese de, bir ağ için parametreleri ilkletebiliyoruz! Örneğimizdeki gibi 2 de olabilir veya 2000 de olabilir. Gluon bunun yanına kalmamıza izin veriyor çünkü sahnenin arkasında, ilk değerleri atama aslında ertelendi. Gerçek ilkleme, yalnızca verileri ağ üzerinden ilk kez geçirmeye çalıştığımızda gerçekleşecektir. Unutmayın ki, parametreler henüz ilkletilmediği için bunlara erişemeyiz veya onları değiştiremeyiz.
nn.Linear
oluştururken girdi ve çıktı boyutlarını belirttik, şimdi,
onların ilk değerlerini belirtmek için parametrelere doğrudan
erişebiliriz. İlk olarak ağdaki ilk katmanı net[0]
ile buluruz ve
ardından parametrelere erişmek için weight.data
ve bias.data
yöntemlerini kullanırız. Daha sonra, parametre değerlerinin üzerine
yazmak için normal_
ve fill_
değiştirme yöntemlerini kullanırız.
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
tensor([0.])
TensorFlow’daki initializers
modülü, model parametresi ilkletme için
çeşitli yöntemler sağlar. Keras’ta ilkletme yöntemini belirlemenin en
kolay yolu, katmanı kernel_initializer
belirterek oluşturmaktır.
Burada net
’i yeniden oluşturuyoruz.
initializer = tf.initializers.RandomNormal(stddev=0.01)
net = tf.keras.Sequential()
net.add(tf.keras.layers.Dense(1, kernel_initializer=initializer))
Yukarıdaki kod basit görünebilir, ancak burada tuhaf bir şeylerin olduğunu fark etmelisiniz. Keras, girdinin kaç boyuta sahip olacağını henüz bilmese de, bir ağ için parametreleri ilkletebiliyoruz! Örneğimizdeki gibi 2 de olabilir veya 2000 de olabilir. Keras bunun yanına kalmamıza izin veriyor çünkü sahnenin arkasında, ilk değerleri atama aslında ertelendi. Gerçek ilkleme, yalnızca verileri ağ üzerinden ilk kez geçirmeye çalıştığımızda gerçekleşecektir. Unutmayın ki, parametreler henüz ilkletilmediği için bunlara erişemeyiz veya onları değiştiremeyiz.
3.3.5. Kayıp Fonksiyonunu Tanımlama¶
Gluon’da loss
modülü çeşitli kayıp fonksiyonlarını tanımlar. Bu
örnekte, Gluon uygulamasının kare kaybını (L2Loss
) kullanacağız.
loss = gluon.loss.L2Loss()
MSELoss
sınıfı, ortalama hata karesini hesaplar ((3.1.5)
içindeki \(1/2\) çarpanı olmadan). Varsayılan olarak, örneklerdeki
ortalama kaybı döndürür.
loss = nn.MSELoss()
MeanSquaredError
sınıfı, ortalama hata karesini hesaplar
((3.1.5) içindeki \(1/2\) çarpanı olmadan). Varsayılan
olarak, örneklerdeki ortalama kaybı döndürür.
loss = tf.keras.losses.MeanSquaredError()
3.3.6. Optimizasyon Algoritmasını Tanımlama¶
Minigrup rasgele gradyan inişi, sinir ağlarını optimize etmek için
standart bir araçtır ve bu nedenle Gluon, bunu Trainer
sınıfı
aracılığıyla bu algoritmadaki bir dizi varyasyonla birlikte destekler.
Trainer
’den örnek yarattığımızda, optimize edilecek parametreleri
(net.collect_params()
aracılığıyla net
modelimizden elde
edilebilir), kullanmak istediğimiz optimizasyon algoritması (sgd
) ve
optimizasyon algoritmamızın gerektirdiği hiper parametreleri bir sözlük
ile (dictionary veri yapısı) belirleyeceğiz. Minigrup rasgele gradyan
inişi, burada 0.03 olarak ayarlanan learning_rate
(öğrenme oranı)
değerini ayarlamamızı gerektirir.
from mxnet import gluon
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.03})
Minigrup rasgele gradyan inişi, sinir ağlarını optimize etmek için
standart bir araçtır ve bu nedenle PyTorch, bunu Trainer
sınıfı
aracılığıyla bu algoritmadaki bir dizi varyasyonla birlikte destekler.
SGD
’den örnek yarattığımızda, optimize edilecek parametreleri
(net.collect_params()
aracılığıyla net
modelimizden elde
edilebilir) ve optimizasyon algoritmamızın gerektirdiği hiper
parametreleri bir sözlük ile (dictionary veri yapısı) belirteceğiz.
Minigrup rasgele gradyan inişi, burada 0.03 olarak ayarlanan lr
(öğrenme oranı) değerini ayarlamamızı gerektirir.
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
Minigrup rasgele gradyan inişi, sinir ağlarını optimize etmek için
standart bir araçtır ve bu nedenle Keras, bunu, optimizers
modülündeki bu algoritmanın bir dizi varyasyonunun yanında destekler.
Minigrup rasgele gradyan inişi, burada 0.03 olarak ayarlanan
learning_rate
(öğrenme oranı) değerini ayarlamamızı gerektirir.
trainer = tf.keras.optimizers.SGD(learning_rate=0.03)
3.3.7. Eğitim¶
Modelimizi derin öğrenme çerçevesinin yüksek seviyeli API’leri aracılığıyla ifade etmenin nispeten birkaç satır kod gerektirdiğini fark etmiş olabilirsiniz. Parametreleri ayrı ayrı tahsis etmemiz, kayıp fonksiyonumuzu tanımlamamız veya minigrup rasgele gradyan inişini uygulamamız gerekmedi. Çok daha karmaşık modellerle çalışmaya başladığımızda, üst düzey API’lerin avantajları önemli ölçüde artacaktır. Bununla birlikte, tüm temel parçaları bir kez yerine getirdiğimizde, eğitim döngüsünün kendisi, her şeyi sıfırdan uygularken yaptığımıza çarpıcı bir şekilde benzer.
Hafızanızı yenilemek için: Bazı dönemler (epoch) için, veri kümesinin
(train_data
) üzerinden eksiksiz bir geçiş yapacağız ve yinelemeli
olarak bir minigrup girdiyi ve ilgili referans gerçek değer etiketleri
alacağız. Her minigrup için aşağıdaki ritüeli uyguluyoruz:
net(X)
’i çağırarak tahminler oluşturun vel
kaybını (ileriye doğru yayma) hesaplayın.Geri yaymayı çalıştırarak gradyanları hesaplayın.
Optimize edicimizi çağırarak model parametrelerini güncelleyin.
İyi bir önlem olarak, her dönemden sonra kaybı hesaplıyor ve ilerlemeyi izlemek için yazdırıyoruz.
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
with autograd.record():
l = loss(net(X), y)
l.backward()
trainer.step(batch_size)
l = loss(net(features), labels)
print(f'donem {epoch + 1}, kayip {l.mean().asnumpy():f}')
[21:43:34] src/base.cc:49: GPU context requested, but no GPUs found.
donem 1, kayip 0.024914
donem 2, kayip 0.000088
donem 3, kayip 0.000051
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X) ,y)
trainer.zero_grad()
l.backward()
trainer.step()
l = loss(net(features), labels)
print(f'donem {epoch + 1}, kayip {l:f}')
donem 1, kayip 0.000174
donem 2, kayip 0.000093
donem 3, kayip 0.000093
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
with tf.GradientTape() as tape:
l = loss(net(X, training=True), y)
grads = tape.gradient(l, net.trainable_variables)
trainer.apply_gradients(zip(grads, net.trainable_variables))
l = loss(net(features), labels)
print(f'donem {epoch + 1}, kayip {l:f}')
donem 1, kayip 0.000525
donem 2, kayip 0.000113
donem 3, kayip 0.000112
Aşağıda, sonlu veri üzerinde eğitimle öğrenilen model parametrelerini ve
veri kümemizi oluşturan gerçek parametreleri karşılaştırıyoruz.
Parametrelere erişmek için önce ihtiyacımız olan katmana net
’ten
erişiyoruz ve sonra bu katmanın ağırlıklarına ve ek girdilerine
erişiyoruz. Sıfırdan uygulamamızda olduğu gibi, tahmin edilen
parametrelerimizin gerçek referans değerlere yakın olduğuna dikkat edin.
w = net[0].weight.data()
print(f'w tahmin hatasi: {true_w - w.reshape(true_w.shape)}')
b = net[0].bias.data()
print(f'b tahmin hatasi: {true_b - b}')
w tahmin hatasi: [0.00022209 0.00029874]
b tahmin hatasi: [0.00032616]
w = net[0].weight.data
print('w tahmin hatasi:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b tahmin hatasi:', true_b - b)
w tahmin hatasi: tensor([ 2.4557e-05, -2.3890e-04])
b tahmin hatasi: tensor([2.3842e-06])
w = net.get_weights()[0]
print('w tahmin hatasi', true_w - tf.reshape(w, true_w.shape))
b = net.get_weights()[1]
print('b tahmin hatasi', true_b - b)
w tahmin hatasi tf.Tensor([ 0.00071025 -0.00022793], shape=(2,), dtype=float32)
b tahmin hatasi [-0.00070047]
3.3.8. Özet¶
Gluon kullanarak modelleri çok daha kısaca uygulayabiliyoruz.
Gluon’da,
data
modülü veri işleme için araçlar sağlar,nn
modülü çok sayıda sinir ağı katmanını tanımlar veloss
modülü birçok yaygın kayıp işlevini tanımlar.MXNet’in
initializer
modülü, model parametresi ilkleme için çeşitli yöntemler sağlar.Boyutluluk ve depolama otomatik olarak çıkarılır, ancak parametrelere ilklemeden önce erişmeye çalışmamaya dikkat edin.
PyTorch’un üst düzey API’lerini kullanarak modelleri çok daha kısa bir şekilde uygulayabiliriz.
PyTorch’ta,
data
modülü veri işleme için araçlar sağlar,nn
modülü çok sayıda sinir ağı katmanını ve genel kayıp işlevlerini tanımlar.Değerlerini
_
ile biten yöntemlerle değiştirerek parametreleri ilkleyebiliriz.
TensorFlow’un üst düzey API’lerini kullanarak modelleri çok daha kısa bir şekilde uygulayabiliriz.
TensorFlow’da,
data
modülü veri işleme için araçlar sağlar,keras
modülü çok sayıda sinir ağı katmanını ve genel kayıp işlevlerini tanımlar.TensorFlow’un
initializers
modülü, model parametresi ilkleme için çeşitli yöntemler sağlar.Boyut ve depolama otomatik olarak çıkarılır (ancak parametrelere ilklemeden önce erişmeye çalışmamaya dikkat edin).
3.3.9. Alıştırmalar¶
l = loss(output, y)
yerinel = loss(output, y).mean()
koyarsak, kodun aynı şekilde davranması içintrainer.step(batch_size)
’itrainer.step(1)
olarak değiştirmemiz gerekir. Neden?gluon.loss
veinit
modüllerinde hangi kayıp işlevlerinin ve ilkleme yöntemlerinin sağlandığını görmek için MXNet belgelerini inceleyin. Kaybı Huber kaybıyla yer değiştirin.dense.weight
’in gradyanına nasıl erişirsiniz?
Eğer
nn.MSELoss()
ınn.MSELoss(reduction='sum')
ile değiştirirsek, kodun aynı şekilde davranması için öğrenme oranını nasıl değiştirebiliriz? Neden?Hangi kayıp işlevlerinin ve ilkleme yöntemlerinin sağlandığını görmek için PyTorch belgelerini inceleyin. Kaybı Huber kaybıyla yer değiştirin.
net[0].weight
’in gradyanına nasıl erişirsiniz?
Hangi kayıp işlevlerinin ve ilkleme yöntemlerinin sağlandığını görmek için TensorFlow belgelerini inceleyin. Kaybı Huber kaybıyla yer değiştirin.