.. _sec_model_construction:
Katmanlar ve Bloklar
====================
Sinir ağlarını ilk tanıttığımızda, tek çıktılı doğrusal modellere
odaklandık. Burada tüm model sadece tek bir nörondan oluşuyor. Tek bir
nöronun (i) bazı girdiler aldığını, (ii) karşılık gelen skaler bir çıktı
ürettiğini ve (iii) ilgili bazı amaç işlevlerini optimize etmek için
güncellenebilen bir dizi ilişkili parametreye sahip olduğunu unutmayın.
Sonra, birden çok çıktıya sahip ağları düşünmeye başladığımızda, tüm bir
nöron katmanını karakterize etmek için vektörleştirilmiş aritmetikten
yararlandık. Tıpkı bireysel nöronlar gibi, katmanlar (i) bir dizi girdi
alır, (ii) karşılık gelen çıktıları üretir ve (iii) bir dizi
ayarlanabilir parametre ile açıklanır. Softmaks bağlanımı üzerinde
çalıştığımızda, tek bir katmanın kendisi modeldi. Bununla birlikte, daha
sonra MLP'leri devreye soktuğumuzda bile, modelin bu aynı temel yapıyı
koruduğunu düşünebiliriz.
İlginç bir şekilde, MLPler için hem tüm model hem de kurucu katmanları
bu yapıyı paylaşır. Tam model ham girdileri (öznitelikleri) alır,
çıktılar (tahminler) üretir ve parametrelere (tüm kurucu katmanlardan
birleşik parametreler) sahiptir. Benzer şekilde, her bir katman
girdileri alır (önceki katman tarafından sağlanır) çıktılar (sonraki
katmana girdiler) üretir ve sonraki katmandan geriye doğru akan sinyale
göre güncellenen bir dizi ayarlanabilir parametreye sahiptir.
Nöronların, katmanların ve modellerin bize işimiz için yeterince
soyutlama sağladığını düşünseniz de, genellikle tek bir katmandan daha
büyük ancak tüm modelden daha küçük olan bileşenler hakkında konuşmayı
uygun bulduğumuz ortaya çıkıyor. Örneğin, bilgisayarla görmede aşırı
popüler olan ResNet-152 mimarisi, yüzlerce katmana sahiptir. Bu
katmanlar, *katman gruplarının* tekrar eden desenlerinden oluşur. Böyle
bir ağın her seferinde bir katman olarak uygulanması sıkıcı bir hal
alabilir. Bu endişe sadece varsayımsal değildir---bu tür tasarım
modelleri pratikte yaygındır. Yukarıda bahsedilen ResNet mimarisi, hem
tanıma hem de tespit için 2015 ImageNet ve COCO bilgisayarla görme
yarışmalarını kazandı :cite:`He.Zhang.Ren.ea.2016` ve birçok görme
görevi için bir ilk tatbik edilen mimari olmaya devam ediyor.
Katmanların çeşitli yinelenen desenlerde düzenlendiği benzer mimariler
artık doğal dil işleme ve konuşma dahil olmak üzere diğer alanlarda da
her yerde mevcuttur.
Bu karmaşık ağları uygulamak için, bir sinir ağı *bloğu* kavramını
sunuyoruz. Bir blok, tek bir katmanı, birden çok katmandan oluşan bir
bileşeni veya tüm modelin kendisini tanımlayabilir! Blok soyutlamayla
çalışmanın bir yararı, bunların genellikle yinelemeli olarak daha büyük
yapay nesnelerle birleştirilebilmeleridir. Bu, :numref:`fig_blocks`
içinde gösterilmiştir. İsteğe bağlı olarak rastgele karmaşıklıkta
bloklar oluşturmak için kod tanımlayarak, şaşırtıcı derecede
sıkıştırılmış kod yazabilir ve yine de karmaşık sinir ağlarını
uygulayabiliriz.
.. _fig_blocks:
.. figure:: ../img/blocks.svg
Çoklu katmanlar bloklara birleştirilir, daha geniş modelleri
oluşturan tekrar eden desenler oluştururlar
Programlama açısından, bir blok *sınıf* ile temsil edilir. Onun herhangi
bir alt sınıfı, girdisini çıktıya dönüştüren ve gerekli parametreleri
depolayan bir ileri yayma yöntemi tanımlamalıdır. Bazı blokların
herhangi bir parametre gerektirmediğini unutmayın. Son olarak,
gradyanları hesaplamak için bir blok geriye yayma yöntemine sahip
olmalıdır. Neyse ki, kendi bloğumuzu tanımlarken otomatik türev almanın
sağladığı bazı perde arkası sihir sayesinde (:numref:`sec_autograd`
içinde tanıtıldı), sadece parametreler ve ileri yayma işlevi hakkında
endişelenmemiz gerekir.
Başlangıç olarak MLPleri uygulamak için kullandığımız kodları yeniden
gözden geçiriyoruz (:numref:`sec_mlp_concise`). Aşağıdaki kod, 256
birim ve ReLU etkinleştirmesine sahip tam bağlı bir gizli katmana sahip
bir ağ oluşturur ve ardından 10 birimle (etkinleştirme işlevi yok) tam
bağlı çıktı katmanı gelir.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
from mxnet import np, npx
from mxnet.gluon import nn
npx.set_np()
net = nn.Sequential()
net.add(nn.Dense(256, activation='relu'))
net.add(nn.Dense(10))
net.initialize()
X = np.random.uniform(size=(2, 20))
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
array([[ 0.06240274, -0.03268593, 0.02582653, 0.02254181, -0.03728798,
-0.04253785, 0.00540612, -0.01364185, -0.09915454, -0.02272737],
[ 0.02816679, -0.03341204, 0.03565665, 0.02506384, -0.04136416,
-0.04941844, 0.01738529, 0.01081963, -0.09932579, -0.01176296]])
Bu örnekte, modelimizi bir ``nn.Sequential`` örneğini oluşturarak
döndürülen nesneyi ``net`` değişkenine atayarak oluşturduk. Daha sonra,
katmanları çalıştırılmaları gereken sırayla eklemek için tekrar tekrar
``add`` işlevini çağırıyoruz. Kısaca, ``nn.Sequential``, Gluon'da bir
blok sunan sınıf olan özel bir ``Block`` türünü tanımlar. Oluşturucu
``Block``'ların sıralı bir listesini tutar. ``add`` işlevi, basitçe her
ardışık ``Block``'un listeye eklenmesini kolaylaştırır. Her katmanın,
kendisinin ``Block``'un alt sınıfı olan ``Dense`` sınıfının bir örneği
olduğuna dikkat edin. İleri yayma (``forward``) işlevi de oldukça
basittir: Listedeki her ``Block`` birbirine zincirlenerek her birinin
çıktısını bir sonrakine girdi olarak iletir. Şimdiye kadar, çıktılarını
elde etmek için modellerimizi ``net(X)`` yapısı aracılığıyla
çağırdığımızı unutmayın. Bu aslında ``Block`` sınıfının ``__call__``
işlevi aracılığıyla elde edilen ustaca bir Python marifeti olan
``net.forward(X)`` için kısaltmadır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
import torch
from torch import nn
from torch.nn import functional as F
net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
X = torch.rand(2, 20)
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
tensor([[-0.2314, 0.0426, -0.1038, -0.0008, 0.1609, -0.0306, 0.0023, -0.0257,
-0.1857, -0.0899],
[-0.1886, 0.0906, -0.1492, -0.0039, 0.1726, 0.0048, -0.0374, -0.0620,
-0.2537, -0.0323]], grad_fn=
)
Bu örnekte, modelimizi bir ``nn.Sequential`` örneğini oluşturarak,
katmanları bağımsız değişken olarak iletilmeleri gereken sırayla
oluşturduk. Kısaca, ``nn.Sequential``, PyTorch'ta bir blok sunan özel
bir sınıf ``Module`` türünü tanımlar. Kurucu ``Module``'lerin sıralı bir
listesini tutar. Tam bağlı iki katmanın her birinin, kendisi de
``Module``'ün bir alt sınıfı olan ``Linear`` sınıfının bir örneği
olduğuna dikkat edin. İleri yayma (``forward``) işlevi de oldukça
basittir: Listedeki her bloğu birbirine zincirleyerek her birinin
çıktısını bir sonrakine girdi olarak iletir. Şimdiye kadar, çıktıları
elde etmek için modellerimizi ``net(X)`` yapısı aracılığıyla
çağırdığımızı unutmayın. Bu aslında ``net.__call__(X)`` için kısa
yoldur.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
import tensorflow as tf
net = tf.keras.models.Sequential([
tf.keras.layers.Dense(256, activation=tf.nn.relu),
tf.keras.layers.Dense(10),
])
X = tf.random.uniform((2, 20))
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
Bu örnekte, modelimizi bir ``keras.models.Sequential`` örneğini
oluşturarak, katmanları bağımsız değişken olarak iletilmeleri gereken
sırayla oluşturduk. Kısaca ``Sequential``, Keras'ta bir blok sunan özel
bir sınıf ``keras.Model``'i tanımlar, ki kurucu ``Model``'lerin sıralı
bir listesini tutar. İki tam bağlı katmanın her birinin, kendisi
``Model``'in alt sınıfı olan ``Dense`` sınıfının bir örneği olduğuna
dikkat edin. İleri yayma (``call``) işlevi de oldukça basittir:
Listedeki her bloğu birbirine zincirleyerek her birinin çıkışını bir
sonrakine girdi olarak iletir. Şimdiye kadar, çıktılarını elde etmek
için modellerimizi ``net(X)`` yapısı aracılığıyla çağırdığımızı
unutmayın. Bu aslında ``Block`` sınıfının ``__call__`` işlevi
aracılığıyla elde edilen ustaca bir Python marifeti olan ``net.call(X)``
için kısaltmadır.
.. raw:: html
.. raw:: html
Özel Yapım Blok
---------------
Bir bloğun nasıl çalıştığına dair sezgiyi geliştirmenin belki de en
kolay yolu, bloğu kendimiz uygulamaktır. Kendi özel yapım bloğumuzu
uygulamadan önce, her bloğun sağlaması gereken temel işlevleri kısaca
özetliyoruz:
.. raw:: html
.. raw:: html
1. Girdi verilerini ileri yayma yöntemine bağımsız değişkenler olarak
alın.
2. İleri yayma işlevi kullanarak bir değer döndürüp bir çıktı oluşturun.
Çıktının girdiden farklı bir şekle sahip olabileceğini unutmayın.
Örneğin, yukarıdaki modelimizdeki ilk tam bağlı katman, rastgele bir
boyut girdisi alır, ancak 256 boyutunda bir çıktı verir.
3. Girdiye göre çıktısının gradyanını hesaplayın, ki bu da geriye yayma
yöntemiyle erişilebilir. Genellikle bu otomatik olarak gerçekleşir.
4. İleri yaymayı hesaplamayı yürütmek için gerekli olan bu parametreleri
saklayın ve bunlara erişim sağlayın.
5. Model parametrelerini gerektiği gibi ilkletin.
.. raw:: html
.. raw:: html
1. Girdi verilerini ileri yayma yöntemine bağımsız değişkenler olarak
alın.
2. İleri yayma işlevi kullanarak bir değer döndürüp bir çıktı oluşturun.
Çıktının girdiden farklı bir şekle sahip olabileceğini unutmayın.
Örneğin, yukarıdaki modelimizdeki ilk tam bağlı katman 20 boyutlu bir
girdi alır, ancak 256 boyutunda bir çıktı verir.
3. Girdiye göre çıktısının gradyanını hesaplayın, ki bu da geriye yayma
yöntemiyle erişilebilir. Genellikle bu otomatik olarak gerçekleşir.
4. İleri yaymayı hesaplamayı yürütmek için gerekli olan bu parametreleri
saklayın ve bunlara erişim sağlayın.
5. Model parametrelerini gerektiği gibi ilkletin.
.. raw:: html
.. raw:: html
1. Girdi verilerini ileri yayma yöntemine bağımsız değişkenler olarak
alın.
2. İleri yayma işlevi kullanarak bir değer döndürüp bir çıktı oluşturun.
Çıktının girdiden farklı bir şekle sahip olabileceğini unutmayın.
Örneğin, yukarıdaki modelimizdeki ilk tam bağlı katman, rastgele bir
boyut girdisi alır, ancak 256 boyutunda bir çıktı verir.
3. Girdiye göre çıktısının gradyanını hesaplayın, ki bu da geriye yayma
yöntemiyle erişilebilir. Genellikle bu otomatik olarak gerçekleşir.
4. İleri yaymayı hesaplamayı yürütmek için gerekli olan bu parametreleri
saklayın ve bunlara erişim sağlayın.
5. Model parametrelerini gerektiği gibi ilkletin.
.. raw:: html
.. raw:: html
Aşağıdaki kod parçacığında, 256 gizli birime sahip bir gizli katman ve
10 boyutlu bir çıktı katmanı ile bir MLP'ye karşılık gelen bir bloğu
sıfırdan kodladık. Aşağıdaki ``MLP`` sınıfının bir bloğu temsil eden
sınıftan kalıtsal çoğaltıldığını unutmayın. Yalnızca kendi kurucumuzu
(Python'daki '**init**' işlevi) ve ileri yayma işlevini sağlayarak,
büyük ölçüde ana sınıfın işlevlerine güveneceğiz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class MLP(nn.Block):
# Model parametreleriyle bir katman ilan edin.
# Burada, tam bağlı iki katman ilan ediyoruz.
def __init__(self, **kwargs):
# Gerekli ilklemeyi gerçekleştirmek için `MLP` üst sınıfının `Block`
# kurucusunu çağırın. Bu şekilde, sınıf örneği yaratma sırasında model
# parametreleri, `params` (daha sonra açıklanacak) gibi diğer fonksiyon
# argümanları da belirtilebilir.
super().__init__(**kwargs)
self.hidden = nn.Dense(256, activation='relu') # Gizli katman
self.out = nn.Dense(10) # Çıktı katmanı
# Modelin ileri yaymasını tanımla, yani `X` girdisine dayalı
# olarak gerekli model çıktısının nasıl döndürüleceğini tanımla.
def forward(self, X):
return self.out(self.hidden(X))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class MLP(nn.Module):
# Model parametreleriyle bir katman tanımlayın.
# Burada tam bağlı iki katman tanımlıyoruz.
def __init__(self):
# Gerekli ilklemeyi gerçekleştirmek için `MLP` üst sınıfının `Module`
# kurucusunu çağırın. Bu şekilde, sınıf örneği yaratma sırasında model parametreleri,
# `params` (daha sonra açıklanacak) gibi diğer fonksiyon argümanları da belirtilebilir.
super().__init__()
self.hidden = nn.Linear(20, 256) # Gizli katman
self.out = nn.Linear(256, 10) # Çıktı katmanı
# Modelin ileri yaymasını tanımla, yani `X` girdisine dayalı
# olarak gerekli model çıktısının nasıl döndürüleceğini tanımla.
def forward(self, X):
# Burada, nn.function modülünde tanımlanan ReLU'nun fonksiyonel
# versiyonunu kullandığımıza dikkat edin.
return self.out(F.relu(self.hidden(X)))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class MLP(tf.keras.Model):
# Model parametreleriyle bir katman tanımlayın.
# Burada tam bağlı iki katman tanımlıyoruz.
def __init__(self):
# Gerekli ilklemeyi gerçekleştirmek için `MLP` üst sınıfının `Model`
# kurucusunu çağırın. Bu şekilde, sınıf örneği yaratma sırasında model parametreleri,
# `params` (daha sonra açıklanacak) gibi diğer fonksiyon argümanları da belirtilebilir.
super().__init__()
# Gizli katman
self.hidden = tf.keras.layers.Dense(units=256, activation=tf.nn.relu)
self.out = tf.keras.layers.Dense(units=10) # Çıktı katmanı
# Modelin ileri yaymasını tanımla, yani `X` girdisine dayalı
# olarak gerekli model çıktısının nasıl döndürüleceğini tanımla.
def call(self, X):
return self.out(self.hidden((X)))
.. raw:: html
.. raw:: html
İlk olarak ileri yayma işlevine odaklanalım. Girdi olarak ``X``'i
aldığını, gizli gösterimi etkinleştirme işlevi uygulanmış olarak
hesapladığını ve logitlerini çıktı verdiğini unutmayın. Bu ``MLP``
uygulamasında, her iki katman da örnek değişkenlerdir. Bunun neden makul
olduğunu anlamak için, iki MLP'yi (``net1`` ve ``net2``)
somutlaştırdığınızı ve bunları farklı veriler üzerinde eğittiğinizi
hayal edin. Doğal olarak, iki farklı öğrenilmiş modeli temsil etmelerini
bekleriz.
MLP'nin katmanlarını kurucuda ilkliyoruz ve daha sonra bu katmanları her
bir ileri yayma işlevi çağrısında çağırıyoruz. Birkaç önemli ayrıntıya
dikkat edin. İlk olarak, özelleştirilmiş ``__init__`` işlevimiz,
``super().__init__()`` aracılığıyla üst sınıfın ``__init__`` işlevini
çağırır ve bizi çoğu bloğa uygulanabilen standart şablon kodunu yeniden
biçimlendirme zahmetinden kurtarır. Daha sonra, tam bağlı iki
katmanımızı ``self.hidden`` ve ``self.out``'a atayarak
somutlaştırıyoruz. Yeni bir operatör uygulamadığımız sürece, geriye
yayma işlevi veya parametre ilkleme konusunda endişelenmemize gerek
olmadığını unutmayın. Sistem bu işlevleri otomatik olarak üretecektir.
Bunu deneyelim.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = MLP()
net.initialize()
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
array([[-0.03989595, -0.10414709, 0.06799038, 0.05245074, 0.0252606 ,
-0.00640342, 0.04182098, -0.01665318, -0.02067345, -0.07863816],
[-0.03612847, -0.07210435, 0.09159479, 0.07890773, 0.02494171,
-0.01028665, 0.01732427, -0.02843244, 0.03772651, -0.06671703]])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = MLP()
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
tensor([[ 0.0986, 0.0048, 0.0926, 0.0814, -0.0960, -0.0417, -0.0494, -0.0037,
0.1997, -0.1000],
[ 0.0230, -0.0899, 0.0527, 0.1209, -0.0111, -0.1575, -0.0703, -0.0153,
0.0516, -0.1078]], grad_fn=
)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = MLP()
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Blok soyutlamanın önemli bir özelliği çok yönlülüğüdür. Katmanlar (tam
bağlı katman sınıfı gibi), tüm modeller (yukarıdaki ``MLP`` sınıfı gibi)
veya ara karmaşıklığın çeşitli bileşenlerini oluşturmak için bir bloğu
alt sınıflara ayırabiliriz. Bu çok yönlülüğü sonraki bölümlerde, mesela
evrişimli sinir ağlarını, ele alırken kullanıyoruz.
Dizili Blok
-----------
Artık ``Sequential`` (dizili) sınıfının nasıl çalıştığına daha yakından
bakabiliriz. ``Sequential``'nin diğer blokları birbirine zincirleme
bağlamak için tasarlandığını hatırlayın. Kendi basitleştirilmiş
``MySequential``'ımızı oluşturmak için, sadece iki anahtar işlev
tanımlamamız gerekir: 1. Blokları birer birer listeye eklemek için bir
işlev. 2. Bir girdiyi blok zincirinden, eklendikleri sırayla iletmek
için bir ileri yayma işlevi.
Aşağıdaki ``MySequential`` sınıfı aynı işlevselliği varsayılan
``Sequential`` sınıfıyla sunar:
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class MySequential(nn.Block):
def add(self, block):
# Burada, `block`, `Block` alt sınıfının bir örneğidir ve eşsiz bir
# ada sahip olduğunu varsayıyoruz. Bunu `Block` sınıfının `_children`
# üye değişkenine kaydederiz ve türü OrderedDict'tir. `MySequential`
# örneği `initialize` işlevini çağırdığında, sistem tüm `_children`
# üyelerini otomatik olarak ilkler.
self._children[block.name] = block
def forward(self, X):
# OrderedDict, üyelerin eklendikleri sırayla işletileceğini garanti eder.
for block in self._children.values():
X = block(X)
return X
``add`` işlevi, sıralı ``_children`` sözlüğüne tek bir blok ekler. Neden
her Gluon ``Block``'unun bir ``_children`` özelliğine sahip olduğunu ve
sadece bir Python listesi tanımlamak yerine bunu neden kullandığımızı
merak edebilirsiniz. Kısacası, ``_children``'in başlıca avantajı,
bloğumuzun parametre ilklemesi sırasında Gluon'un, parametreleri de
ilklemesi gereken alt blokları bulmak için ``_children`` sözlüğüne
bakmayı bilmesidir.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class MySequential(nn.Module):
def __init__(self, *args):
super().__init__()
for idx, module in enumerate(args):
# Burada, `module` , `mMdule` alt sınıfının bir örneğidir.
# Bunu, `Module` sınıfının `_modules` üye değişkenine kaydederiz
# ve türü OrderedDict'tir.
self._modules[str(idx)] = module
def forward(self, X):
# OrderedDict, üyelerin eklendikleri sırayla işletileceğini garanti eder.
for block in self._modules.values():
X = block(X)
return X
``__init__`` yönteminde, her modülü sıralı ``_modules`` sözlüğüne tek
tek ekliyoruz. Neden her ``Module``'ün bir ``_modules`` özelliğine sahip
olduğunu ve sadece bir Python listesi tanımlamak yerine bunu neden
kullandığımızı merak edebilirsiniz. Kısacası, ``_modules``'ün başlıca
avantajı, modülün parametre ilklemesi sırasında, sistemin, parametreleri
de ilklemesi gereken alt modülleri bulmak için ``_modules`` sözlüğüne
bakmayı bilmesidir.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class MySequential(tf.keras.Model):
def __init__(self, *args):
super().__init__()
self.modules = []
for block in args:
# Burada, `block`, bir `tf.keras.layers.Layer` alt sınıfının bir örneğidir
self.modules.append(block)
def call(self, X):
for module in self.modules:
X = module(X)
return X
.. raw:: html
.. raw:: html
``MySequential``'ımızın ileri yayma işlevi çağrıldığında, eklenen her
blok eklendikleri sırayla yürütülür. Artık ``MySequential`` sınıfımızı
kullanarak bir MLP'yi yeniden uygulayabiliriz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = MySequential()
net.add(nn.Dense(256, activation='relu'))
net.add(nn.Dense(10))
net.initialize()
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
array([[-0.0764568 , -0.01130233, 0.04952145, -0.04651389, -0.04131571,
-0.05884131, -0.06213811, 0.01311471, -0.01379425, -0.02514282],
[-0.05124623, 0.00711232, -0.00155933, -0.07555379, -0.06675334,
-0.01762914, 0.00589085, 0.0144719 , -0.04330775, 0.03317727]])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = MySequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
tensor([[ 0.1398, 0.1564, -0.0464, -0.0877, 0.2311, 0.1336, -0.0970, -0.0086,
0.0315, -0.0298],
[ 0.1132, 0.0785, -0.1431, 0.0261, 0.2176, -0.0221, -0.1219, 0.0359,
0.0496, -0.0944]], grad_fn=
)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = MySequential(
tf.keras.layers.Dense(units=256, activation=tf.nn.relu),
tf.keras.layers.Dense(10))
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Bu ``MySequential`` kullanımının, daha önce ``Sequential`` sınıfı için
yazdığımız kodla aynı olduğuna dikkat edin (:numref:`sec_mlp_concise`
içinde açıklandığı gibi).
İleri Yayma İşlevinde Kodu Yürütme
----------------------------------
``Sequential`` sınıfı, model yapımını kolaylaştırarak, kendi sınıfımızı
tanımlamak zorunda kalmadan yeni mimarileri bir araya getirmemize olanak
tanır. Bununla birlikte, tüm mimariler basit papatya zinciri değildir.
Daha fazla esneklik gerektiğinde, kendi bloklarımızı tanımlamak
isteyeceğiz. Örneğin, Python'un kontrol akışını ileri yayma işlevi ile
yürütmek isteyebiliriz. Dahası, önceden tanımlanmış sinir ağı
katmanlarına güvenmek yerine, keyfi matematiksel işlemler
gerçekleştirmek isteyebiliriz.
Şimdiye kadar ağlarımızdaki tüm işlemlerin ağımızın etkinleştirmelerine
ve parametrelerine göre hareket ettiğini fark etmiş olabilirsiniz. Ancak
bazen, ne önceki katmanların sonucu ne de güncellenebilir parametrelerin
sonucu olmayan terimleri dahil etmek isteyebiliriz. Bunlara *sabit
parametreler* diyoruz. Örneğin,
:math:`f(\mathbf{x},\mathbf{w}) = c \cdot \mathbf{w}^\top \mathbf{x}`
işlevini hesaplayan bir katman istediğimizi varsayalım, burada
:math:`\mathbf{x}` girdi, :math:`\mathbf{w}` bizim parametremiz ve
:math:`c` optimizasyon sırasında güncellenmeyen belirli bir sabittir. Bu
yüzden ``FixedHiddenMLP`` sınıfını aşağıdaki gibi uyguluyoruz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class FixedHiddenMLP(nn.Block):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# `get_constant` işleviyle oluşturulan rastgele ağırlık parametreleri
# eğitim sırasında güncellenmez (yani sabit parametreler)
self.rand_weight = self.params.get_constant(
'rand_weight', np.random.uniform(size=(20, 20)))
self.dense = nn.Dense(20, activation='relu')
def forward(self, X):
X = self.dense(X)
# Oluşturulan sabit parametrelerin yanı sıra `relu` ve `dot`
# işlevlerini kullanın
X = npx.relu(np.dot(X, self.rand_weight.data()) + 1)
# Tam bağlı katmanı yeniden kullanın. Bu, parametreleri tamamen bağlı iki katmanla paylaşmaya eşdeğerdir.
X = self.dense(X)
# Kontrol akışı
while np.abs(X).sum() > 1:
X /= 2
return X.sum()
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class FixedHiddenMLP(nn.Module):
def __init__(self):
super().__init__()
# Gradyanları hesaplamayan ve bu nedenle eğitim sırasında sabit kalan
# rastgele ağırlık parametreleri
self.rand_weight = torch.rand((20, 20), requires_grad=False)
self.linear = nn.Linear(20, 20)
def forward(self, X):
X = self.linear(X)
# Oluşturulan sabit parametrelerin yanı sıra `relu` ve `mm`işlevlerini kullanın
X = F.relu(torch.mm(X, self.rand_weight) + 1)
# Tam bağlı katmanı yeniden kullanın. Bu, parametreleri tamamen bağlı iki
# katmanla paylaşmaya eşdeğerdir.
X = self.linear(X)
# Kontrol akışı
while X.abs().sum() > 1:
X /= 2
return X.sum()
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class FixedHiddenMLP(tf.keras.Model):
def __init__(self):
super().__init__()
self.flatten = tf.keras.layers.Flatten()
# `tf.constant` ile oluşturulan rastgele ağırlık parametreleri eğitim sırasında güncellenmez (yani sabit parametreler)
self.rand_weight = tf.constant(tf.random.uniform((20, 20)))
self.dense = tf.keras.layers.Dense(20, activation=tf.nn.relu)
def call(self, inputs):
X = self.flatten(inputs)
# Oluşturulan sabit parametrelerin yanı sıra `relu` ve `matmul`
# işlevlerini kullanın
X = tf.nn.relu(tf.matmul(X, self.rand_weight) + 1)
# Tam bağlı katmanı yeniden kullanın. Bu, parametreleri tamamen bağlı iki
# katmanla paylaşmaya eşdeğerdir.
X = self.dense(X)
# Kontrol akışı
while tf.reduce_sum(tf.math.abs(X)) > 1:
X /= 2
return tf.reduce_sum(X)
.. raw:: html
.. raw:: html
Bu ``FixedHiddenMLP`` modelinde, ağırlıkları (``self.rand_weight``)
örneklemede rastgele ilkletilen ve daha sonra sabit olan gizli bir
katman uygularız. Bu ağırlık bir model parametresi değildir ve bu
nedenle asla geri yayma ile güncellenmez. Ağ daha sonra bu "sabit"
katmanın çıktısını tam bağlı bir katmandan geçirir.
Çıktıyı döndürmeden önce, modelimizin olağandışı bir şey yaptığını
unutmayın. Bir while-döngüsü çalıştırdık, :math:`L_1` normunun
:math:`1`'den büyük olması koşulunu test ettik ve çıktı vektörümüzü bu
koşulu karşılayana kadar :math:`2`'ye böldük. Son olarak, ``X``'deki
girdilerin toplamını döndürdük. Bildiğimiz kadarıyla hiçbir standart
sinir ağı bu işlemi gerçekleştirmez. Bu özel işlemin herhangi bir gerçek
dünya sorununda yararlı olmayabileceğini unutmayın. Amacımız, yalnızca
rastgele kodu sinir ağı hesaplamalarınızın akışına nasıl
tümleştirebileceğinizi göstermektir.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = FixedHiddenMLP()
net.initialize()
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
array(0.52637565)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = FixedHiddenMLP()
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
tensor(0.0956, grad_fn=)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = FixedHiddenMLP()
net(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Blokları bir araya getirmenin çeşitli yollarını karıştırıp
eşleştirebiliriz. Aşağıdaki örnekte, blokları birtakım yaratıcı yollarla
iç içe yerleştiriyoruz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class NestMLP(nn.Block):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.net = nn.Sequential()
self.net.add(nn.Dense(64, activation='relu'),
nn.Dense(32, activation='relu'))
self.dense = nn.Dense(16, activation='relu')
def forward(self, X):
return self.dense(self.net(X))
chimera = nn.Sequential()
chimera.add(NestMLP(), nn.Dense(20), FixedHiddenMLP())
chimera.initialize()
chimera(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
array(0.9772054)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class NestMLP(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(nn.Linear(20, 64), nn.ReLU(),
nn.Linear(64, 32), nn.ReLU())
self.linear = nn.Linear(32, 16)
def forward(self, X):
return self.linear(self.net(X))
chimera = nn.Sequential(NestMLP(), nn.Linear(16, 20), FixedHiddenMLP())
chimera(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
tensor(0.1101, grad_fn=)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class NestMLP(tf.keras.Model):
def __init__(self):
super().__init__()
self.net = tf.keras.Sequential()
self.net.add(tf.keras.layers.Dense(64, activation=tf.nn.relu))
self.net.add(tf.keras.layers.Dense(32, activation=tf.nn.relu))
self.dense = tf.keras.layers.Dense(16, activation=tf.nn.relu)
def call(self, inputs):
return self.dense(self.net(inputs))
chimera = tf.keras.Sequential()
chimera.add(NestMLP())
chimera.add(tf.keras.layers.Dense(20))
chimera.add(FixedHiddenMLP())
chimera(X)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Verimlilik
----------
.. raw:: html
.. raw:: html
Hevesli okuyucu, bu işlemlerden bazılarının verimliliği konusunda
endişelenmeye başlayabilir. Sonuçta, yüksek performanslı bir derin
öğrenme kütüphanesinde yer alan çok sayıda sözlük arama, kod yürütme ve
daha birçok Pythonik (Python dilince) şeyimiz var. Python'un `global
yorumlayıcı
kilidi `__ sorunları
iyi bilinmektedir. Derin öğrenme bağlamında, son derece hızlı
GPU'larımızın, cılız bir CPU'nun Python kodunu çalıştırması için başka
bir işe girmeden önce onu beklemeleri gerekebileceğinden
endişeleniyoruz. Python'u hızlandırmanın en iyi yolu, ondan tamamen
kaçınmaktır.
Gluon'un daha sonra tanımlayacağımız *melezleştirmeye* izin vermesi bunu
yapmanın bir yoludur. Burada, Python yorumlayıcısı, ilk
çalıştırıldığında bir blok yürütür. Gluon çalışma zamanında neler
olduğunu kaydeder ve bir dahaki sefere kısa devre yaparak Python'a çağrı
yapar. Bu, bazı durumlarda işleri önemli ölçüde hızlandırabilir, ancak
kontrol akışı (yukarıdaki gibi) ağdan farklı geçişlerde farklı dallara
yol açtığı zaman dikkatli olunması gerekir. İlgili okuyucunun, mevcut
bölümü bitirdikten sonra derleme hakkında bilgi edinmek için
melezleştirme bölümüne (:numref:`sec_hybridize`) bakmasını öneririz.
.. raw:: html
.. raw:: html
Hevesli okuyucular, bu işlemlerin bazılarının verimliliği konusunda
endişe duymaya başlayabilir. Ne de olsa, yüksek performanslı bir derin
öğrenme kitaplığı olması gereken yerde çok sayıda sözlük araması, kod
koşturma ve birçok başka Pythonik şey var. Python'un `genel yorumlayıcı
kilidi `__ sorunları
iyi bilinmektedir. Derin öğrenme bağlamında, son derece hızlı
GPU'larımızın, başka bir işi çalıştırmadan önce cılız bir CPU'nun Python
kodunu çalıştırmasını beklemesi gerekebileceğinden endişe duyabiliriz.
.. raw:: html
.. raw:: html
Hevesli okuyucular, bu işlemlerin bazılarının verimliliği konusunda
endişe duymaya başlayabilir. Ne de olsa, yüksek performanslı bir derin
öğrenme kitaplığı olması gereken yerde çok sayıda sözlük araması, kod
koşturma ve birçok başka Pythonik şey var. Python'un `genel yorumlayıcı
kilidi `__ sorunları
iyi bilinmektedir. Derin öğrenme bağlamında, son derece hızlı
GPU'larımızın, başka bir işi çalıştırmadan önce cılız bir CPU'nun Python
kodunu çalıştırmasını beklemesi gerekebileceğinden endişe duyabiliriz.
Python'u hızlandırmanın en iyi yolu, ondan tamamen kaçınmaktır.
.. raw:: html
.. raw:: html
Özet
----
- Katmanlar bloklardır.
- Birçok katman bir blok içerebilir.
- Bir blok birçok blok içerebilir.
- Bir blok kod içerebilir.
- Bloklar, parametre ilkleme ve geri yayma dahil olmak üzere birçok
temel yürütme işlerini halleder.
- Katmanların ve blokların dizili birleştirmeleri ``Sequential`` blok
tarafından gerçekleştirilir.
Alıştırmalar
------------
1. Blokları bir Python listesinde saklamak için ``MySequential``'ı
değiştirirseniz ne tür sorunlar ortaya çıkacaktır?
2. Bağımsız değişken olarak iki blok alan bir blok uygulayın, örneğin
``net1`` ve ``net2`` ve ileri yaymada her iki ağın birleştirilmiş
çıktısını döndürsün. Buna paralel blok da denir.
3. Aynı ağın birden çok örneğini birleştirmek istediğinizi varsayın.
Aynı bloğun birden çok örneğini oluşturan ve ondan daha büyük bir ağ
oluşturan bir fabrika işlevi uygulayın.
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html