.. _sec_resnet:
Artık Ağlar (ResNet)
====================
Giderek daha derin ağlar tasarlarken, katman eklemenin ağın
karmaşıklığını ve ifade gücünü nasıl artırabileceğini anlamak zorunluluk
haline gelmektedir. Daha da önemlisi, katmanlar ekleyerek ağları sadece
farklı olmaktan çok daha açıklayıcı hale getiren ağları tasarlama
yeteneğidir. Biraz ilerleme sağlamak için bir parça matematiğe
ihtiyacımız var.
Fonksiyon Sınıfları
-------------------
Belirli bir ağ mimarisinin (öğrenme hızları ve diğer hiper parametre
ayarları ile birlikte) ulaşabileceği işlevlerin sınıfı olan
:math:`\mathcal{F}`'i düşünün. Yani, :math:`f \in \mathcal{F}`'in tümü
için uygun bir veri kümesi üzerinde eğitim yoluyla elde edilebilecek
bazı parametreler (örneğin, ağırlıklar ve ek girdiler) vardır.
:math:`f^*`'in gerçekten bulmak istediğimiz “gerçek” fonksiyon olduğunu
varsayalım. Eğer :math:`\mathcal{F}`'te ise, iyi durumdayız ancak tipik
olarak bu kadar şanslı olmayacağız. Bunun yerine, :math:`\mathcal{F}`
içinde en iyi aday olan herhangi bir :math:`f^*_\mathcal{F}` bulmaya
çalışacağız. Örneğin, :math:`\mathbf{X}` öznitelikleri ve
:math:`\mathbf{y}` etiketleri olan bir veri kümesi göz önüne
alındığında, aşağıdaki eniyileme sorununu çözerek aday bulmayı
deneyebiliriz:
.. math:: f^*_\mathcal{F} \stackrel{\mathrm{def}}{=} \mathop{\mathrm{argmin}}_f L(\mathbf{X}, \mathbf{y}, f) \text{ öyle ki } f \in \mathcal{F}.
Sadece farklı ve daha güçlü bir :math:`\mathcal{F}'` mimarisi
tasarlarsak daha iyi bir sonuca ulaşacağımızı varsaymak mantıklıdır.
Başka bir deyişle, :math:`f^*_{\mathcal{F}'}`'nin
:math:`f^*_{\mathcal{F}}`'dan “daha iyi” olmasını bekleriz. Ancak, eğer
:math:`\mathcal{F} \not\subseteq \mathcal{F}'` ise, bunun gerçekleşmesi
gerektiğinin garantisi yoktur. Aslında, :math:`f^*_{\mathcal{F}'}` daha
kötü olabilir. :numref:`fig_functionclasses` içinde gösterildiği gibi,
iç içe olmayan işlev sınıfları için, daha büyük bir işlev sınıfı her
zaman “gerçek” işlevi :math:`f^*`'ye yakınlaşmaz. Örneğin,
:numref:`fig_functionclasses` solunda :math:`\mathcal{F}_3`'ün
:math:`f^*`'a :math:`\mathcal{F}_1`'den daha yakın olmasına rağmen,
:math:`\mathcal{F}_6` uzaklaşıyor ve karmaşıklığın daha da
arttırılmasının :math:`f^*`'ye mesafeyi azaltabileceğinin garantisi
yoktur. İç içe işlev sınıfları ile,
:math:`\mathcal{F}_1 \subseteq \ldots \subseteq \mathcal{F}_6`, burada
:numref:`fig_functionclasses` sağında gösteriliyor, yukarıda
bahsedilen iç içe olmayan işlev sınıfları sorunundan kaçınabilirsiniz.
.. _fig_functionclasses:
.. figure:: ../img/functionclasses.svg
İç içe olmayan işlev sınıfları için, daha büyük (alanla gösteriliyor)
işlev sınıfı, "doğruluk" işlevine (:math:`f^*`) yaklaşmayı garanti
etmez. Bu, yuvalanmış işlev sınıflarında gerçekleşmez.
Böylece, daha büyük fonksiyon sınıfları daha küçük olanları içeriyorsa,
onları artırmanın ağın açıklayıcı gücünü kesinlikle arttırdığını garanti
ederiz. Derin sinir ağları için, yeni eklenen katmanı bir birim
fonksiyonu :math:`f(\mathbf{x}) = \mathbf{x}` olarak eğitebilirsek, yeni
model orijinal model kadar etkili olacaktır. Yeni model, eğitim veri
kümesine uyacak şekilde daha iyi bir çözüm elde edebileceğinden, eklenen
katman eğitim hatalarını azaltmayı kolaylaştırabilir.
Bu çok derin bilgisayarla görme modelleri üzerinde çalışırken He ve
diğerlerinin :cite:`He.Zhang.Ren.ea.2016` üstünde düşündüğü sorudur.
Önerilen *artık ağ*\ ın (*ResNet*) özünde, her ek katmanın kendi
elemanlarından biri olarak birim işlevini daha kolaylıkla içermesi
gerektiği fikri vardır. Bu fikirler oldukça bilgeydi, fakat şaşırtıcı
derecede basit bir çözüme ulaştılar; *artık blok*. ResNet, 2015 yılında
ImageNet Büyük Ölçekli Görsel Tanıma Yarışmasını kazandı. Tasarımın
derin sinir ağlarının nasıl kurulacağı üzerinde derin bir etkisi oldu.
Artık Blokları
--------------
:numref:`fig_residual_block` içinde gösterildiği gibi, bir sinir
ağının yerel bir bölümüne odaklanalım. Girdiyi :math:`\mathbf{x}` ile
belirtelim. Öğrenerek elde etmek istediğimiz altta yatan eşlemenin
üstteki etkinleştirme işlevine girdi olarak kullanılacak
:math:`f(\mathbf{x})` olduğunu varsayıyoruz.
:numref:`fig_residual_block` solunda, kesik çizgili kutudaki kısım
doğrudan :math:`f(\mathbf{x})` eşlemesini öğrenmelidir. Sağda, kesik
çizgili kutudaki bölümün, artık bloğun adını belirleyen *artık eşleme*
:math:`f(\mathbf{x}) - \mathbf{x}`'i öğrenmesi gerekir. Birim eşlemesi
:math:`f(\mathbf{x}) = \mathbf{x}` istenen altta yatan eşleme ise, artık
eşlemeyi öğrenmek daha kolaydır: Sadece kesik çizgili kutudaki üst
ağırlık katmanının ağırlıklarını ve ek girdilerini (örn. tam bağlı
katman ve evrişimli katman) sıfıra itmemiz gerekir.
:numref:`fig_residual_block` içindeki sağdaki şekil, ResNet'in *artık
bloğu*\ nu göstermektedir; burada :math:`\mathbf{x}` katman girdisini
toplama işlemine taşıyan düz çizgiye *artık bağlantı* (veya *kısayol
bağlantısı*) denir. Artık bloklarla girdiler, katmanlar arasında kalan
bağlantılar üzerinden daha hızlı yayılabilir.
.. _fig_residual_block:
.. figure:: ../img/residual-block.svg
Normal bir blok (solda) ve bir artık blok (sağda).
ResNet, VGG'nin :math:`3\times 3` evrişimli katman tam tasarımını izler.
Artık blok, aynı sayıda çıktı kanalına sahip iki :math:`3\times 3`
evrişimli katmana sahiptir. Her bir evrişimli katmanı, bir toplu
normalleştirme katmanı ve bir ReLU etkinleştirme işlevi izler. Ardından,
bu iki evrişim işlemini atlayıp girdiyi doğrudan son ReLU etkinleştirme
işlevinden önce ekleriz. Bu tür bir tasarım, iki evrişimli katmanın
çıktısının girdiyle aynı şekle sahip olmasını gerektirir, ancak böylece
birlikte toplanabilirler. Kanal sayısını değiştirmek istiyorsak, girdiyi
toplama işlemi için istenen şekle dönüştürürken ek bir :math:`1\times 1`
evrişimli katman koymamız gerekir. Aşağıdaki koda bir göz atalım.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
from d2l import mxnet as d2l
from mxnet import np, npx
from mxnet.gluon import nn
npx.set_np()
class Residual(nn.Block): #@save
"""ResNet'in artık bloğu."""
def __init__(self, num_channels, use_1x1conv=False, strides=1, **kwargs):
super().__init__(**kwargs)
self.conv1 = nn.Conv2D(num_channels, kernel_size=3, padding=1,
strides=strides)
self.conv2 = nn.Conv2D(num_channels, kernel_size=3, padding=1)
if use_1x1conv:
self.conv3 = nn.Conv2D(num_channels, kernel_size=1,
strides=strides)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm()
self.bn2 = nn.BatchNorm()
def forward(self, X):
Y = npx.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
return npx.relu(Y + X)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l
class Residual(nn.Module): #@save
"""ResNet'in artık bloğu."""
def __init__(self, input_channels, num_channels,
use_1x1conv=False, strides=1):
super().__init__()
self.conv1 = nn.Conv2d(input_channels, num_channels,
kernel_size=3, padding=1, stride=strides)
self.conv2 = nn.Conv2d(num_channels, num_channels,
kernel_size=3, padding=1)
if use_1x1conv:
self.conv3 = nn.Conv2d(input_channels, num_channels,
kernel_size=1, stride=strides)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm2d(num_channels)
self.bn2 = nn.BatchNorm2d(num_channels)
def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
Y += X
return F.relu(Y)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
import tensorflow as tf
from d2l import tensorflow as d2l
class Residual(tf.keras.Model): #@save
"""ResNet'in artık bloğu."""
def __init__(self, num_channels, use_1x1conv=False, strides=1):
super().__init__()
self.conv1 = tf.keras.layers.Conv2D(
num_channels, padding='same', kernel_size=3, strides=strides)
self.conv2 = tf.keras.layers.Conv2D(
num_channels, kernel_size=3, padding='same')
self.conv3 = None
if use_1x1conv:
self.conv3 = tf.keras.layers.Conv2D(
num_channels, kernel_size=1, strides=strides)
self.bn1 = tf.keras.layers.BatchNormalization()
self.bn2 = tf.keras.layers.BatchNormalization()
def call(self, X):
Y = tf.keras.activations.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3 is not None:
X = self.conv3(X)
Y += X
return tf.keras.activations.relu(Y)
.. raw:: html
.. raw:: html
Bu kod iki tür ağ oluşturur: Biri, ``use_1x1conv=False``'te ReLU
doğrusal olmayanlığını uygulamadan önce çıktı ile girdiyi topladığımız
ve diğeri toplamadan önce :math:`1 \times 1` evrişim vasıtasıyla
kanalları ve çözünürlüğü ayarladığımız. :numref:`fig_resnet_block`
içinde bunu görebiliriz:
.. _fig_resnet_block:
.. figure:: ../img/resnet-block.svg
:math:`1 \times 1` evrişim içeren ve içermeyen ResNet bloğu.
Şimdi girdinin ve çıktının aynı şekle sahip olduğu bir duruma bakalım.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = Residual(3)
blk.initialize()
X = np.random.uniform(size=(4, 3, 6, 6))
blk(X).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(4, 3, 6, 6)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = Residual(3,3)
X = torch.rand(4, 3, 6, 6)
Y = blk(X)
Y.shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
torch.Size([4, 3, 6, 6])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = Residual(3)
X = tf.random.uniform((4, 6, 6, 3))
Y = blk(X)
Y.shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
TensorShape([4, 6, 6, 3])
.. raw:: html
.. raw:: html
Ayrıca çıktı kanallarının sayısını artırırken çıktı yüksekliğini ve
genişliğini yarıya indirme seçeneğine de sahibiz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = Residual(6, use_1x1conv=True, strides=2)
blk.initialize()
blk(X).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(4, 6, 3, 3)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = Residual(3,6, use_1x1conv=True, strides=2)
blk(X).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
torch.Size([4, 6, 3, 3])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = Residual(6, use_1x1conv=True, strides=2)
blk(X).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
TensorShape([4, 3, 3, 6])
.. raw:: html
.. raw:: html
ResNet Modeli
-------------
ResNet'in ilk iki katmanı, daha önce tarif ettiğimiz GoogLeNet'inkiyle
aynıdır: 64 çıktı kanallı ve 2'lik bir uzun adımlı :math:`7\times 7`
evrişimli katmanını 2'lik bir uzun adımlı :math:`3\times 3` maksimum
ortaklama katmanı takip eder. Fark, ResNet'teki her evrişimli katmandan
sonra eklenen toplu normalleştirme katmanıdır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = nn.Sequential()
net.add(nn.Conv2D(64, kernel_size=7, strides=2, padding=3),
nn.BatchNorm(), nn.Activation('relu'),
nn.MaxPool2D(pool_size=3, strides=2, padding=1))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(64), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
b1 = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(64, kernel_size=7, strides=2, padding='same'),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Activation('relu'),
tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same')])
.. raw:: html
.. raw:: html
GoogLeNet, başlangıç bloklarından oluşan dört modül kullanır. Bununla
birlikte, ResNet, her biri aynı sayıda çıktı kanalına sahip içinde
birkaç artık blok kullanan artık bloklardan oluşan dört modül kullanır.
İlk modüldeki kanal sayısı, girdi kanallarının sayısı ile aynıdır. 2'lik
uzun adımlı maksimum bir ortaklama katmanı kullanıldığından, yüksekliği
ve genişliği azaltmak gerekli değildir. Sonraki modüllerin her biri için
ilk artık blokta, kanal sayısı önceki modülünkine kıyasla iki katına
çıkarılır ve yükseklik ve genişlik yarıya indirilir.
Şimdi, bu modülü uyguluyoruz. İlk modülde özel işlemenin
gerçekleştirildiğine dikkat edin.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def resnet_block(num_channels, num_residuals, first_block=False):
blk = nn.Sequential()
for i in range(num_residuals):
if i == 0 and not first_block:
blk.add(Residual(num_channels, use_1x1conv=True, strides=2))
else:
blk.add(Residual(num_channels))
return blk
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def resnet_block(input_channels, num_channels, num_residuals,
first_block=False):
blk = []
for i in range(num_residuals):
if i == 0 and not first_block:
blk.append(Residual(input_channels, num_channels,
use_1x1conv=True, strides=2))
else:
blk.append(Residual(num_channels, num_channels))
return blk
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class ResnetBlock(tf.keras.layers.Layer):
def __init__(self, num_channels, num_residuals, first_block=False,
**kwargs):
super(ResnetBlock, self).__init__(**kwargs)
self.residual_layers = []
for i in range(num_residuals):
if i == 0 and not first_block:
self.residual_layers.append(
Residual(num_channels, use_1x1conv=True, strides=2))
else:
self.residual_layers.append(Residual(num_channels))
def call(self, X):
for layer in self.residual_layers.layers:
X = layer(X)
return X
.. raw:: html
.. raw:: html
Ardından, tüm modülleri ResNet'e ekliyoruz. Burada, her modül için iki
artık blok kullanılır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net.add(resnet_block(64, 2, first_block=True),
resnet_block(128, 2),
resnet_block(256, 2),
resnet_block(512, 2))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True))
b3 = nn.Sequential(*resnet_block(64, 128, 2))
b4 = nn.Sequential(*resnet_block(128, 256, 2))
b5 = nn.Sequential(*resnet_block(256, 512, 2))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
b2 = ResnetBlock(64, 2, first_block=True)
b3 = ResnetBlock(128, 2)
b4 = ResnetBlock(256, 2)
b5 = ResnetBlock(512, 2)
.. raw:: html
.. raw:: html
Son olarak, tıpkı GoogLeNet gibi global bir ortalama ortaklama katmanı
ekliyoruz ve ardına tam bağlı çıktı katmanı koyuyoruz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net.add(nn.GlobalAvgPool2D(), nn.Dense(10))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = nn.Sequential(b1, b2, b3, b4, b5,
nn.AdaptiveAvgPool2d((1,1)),
nn.Flatten(), nn.Linear(512, 10))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
# Bunu daha sonra yeniden kullanabilmemiz ve çeşitli hesaplama kaynaklarını
# kullanmak için `tf.distribute.MirroredStrategy` kapsamında çalıştırabilmemiz
# için bir işlev olarak tanımladığımızı hatırlayın, mesela GPU'lar. Ayrıca b1,
# b2, b3, b4, b5'i yaratmış olmamıza rağmen, onları bu fonksiyonun kapsamı
# içinde yeniden oluşturacağımızı unutmayın.
def net():
return tf.keras.Sequential([
# Aşağıdaki katmanlar daha önce oluşturduğumuz b1 ile aynıdır
tf.keras.layers.Conv2D(64, kernel_size=7, strides=2, padding='same'),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Activation('relu'),
tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same'),
# Aşağıdaki katmanlar daha önce oluşturduğumuz b2, b3, b4 ve b5 ile
# aynıdır
ResnetBlock(64, 2, first_block=True),
ResnetBlock(128, 2),
ResnetBlock(256, 2),
ResnetBlock(512, 2),
tf.keras.layers.GlobalAvgPool2D(),
tf.keras.layers.Dense(units=10)])
.. raw:: html
.. raw:: html
Her modülde 4 evrişimli katman vardır (:math:`1\times 1` evrişimli
katman hariç). İlk :math:`7\times 7` evrişimli katman ve son tam bağlı
tabaka ile birlikte toplamda 18 katman vardır. Bu nedenle, bu model
yaygın olarak ResNet-18 olarak bilinir. Modülde farklı sayıda kanal ve
artık blokları yapılandırarak, daha derin 152 katmanlı ResNet-152 gibi
farklı ResNet modelleri oluşturabiliriz. ResNet'in ana mimarisi
GoogLeNet'e benzer olsa da, ResNet'in yapısı daha basit ve değiştirmesi
daha kolaydır. Tüm bu faktörler ResNet'in hızlı ve yaygın kullanımı ile
sonuçlandı. :numref:`fig_resnet18` içinde bütün ResNet-18'i
görselleştiriyoruz.
.. _fig_resnet18:
.. figure:: ../img/resnet18.svg
The ResNet-18 architecture.
ResNet'i eğitmeden önce, girdi şeklinin ResNet'teki farklı modüllerde
nasıl değiştiğini gözlemleyelim. Önceki tüm mimarilerinde olduğu gibi,
çözünürlük azalırken, kanal sayısı küresel ortalama ortaklama katmanının
tüm öznitelikleri topladığı noktaya kadar artar.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
X = np.random.uniform(size=(1, 1, 224, 224))
net.initialize()
for layer in net:
X = layer(X)
print(layer.name, 'output shape:\t', X.shape)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
conv5 output shape: (1, 64, 112, 112)
batchnorm4 output shape: (1, 64, 112, 112)
relu0 output shape: (1, 64, 112, 112)
pool0 output shape: (1, 64, 56, 56)
sequential1 output shape: (1, 64, 56, 56)
sequential2 output shape: (1, 128, 28, 28)
sequential3 output shape: (1, 256, 14, 14)
sequential4 output shape: (1, 512, 7, 7)
pool1 output shape: (1, 512, 1, 1)
dense0 output shape: (1, 10)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
X = torch.rand(size=(1, 1, 224, 224))
for layer in net:
X = layer(X)
print(layer.__class__.__name__,'output shape:\t', X.shape)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
Sequential output shape: torch.Size([1, 64, 56, 56])
Sequential output shape: torch.Size([1, 64, 56, 56])
Sequential output shape: torch.Size([1, 128, 28, 28])
Sequential output shape: torch.Size([1, 256, 14, 14])
Sequential output shape: torch.Size([1, 512, 7, 7])
AdaptiveAvgPool2d output shape: torch.Size([1, 512, 1, 1])
Flatten output shape: torch.Size([1, 512])
Linear output shape: torch.Size([1, 10])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
X = tf.random.uniform(shape=(1, 224, 224, 1))
for layer in net().layers:
X = layer(X)
print(layer.__class__.__name__,'output shape:\t', X.shape)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
Conv2D output shape: (1, 112, 112, 64)
BatchNormalization output shape: (1, 112, 112, 64)
Activation output shape: (1, 112, 112, 64)
MaxPooling2D output shape: (1, 56, 56, 64)
ResnetBlock output shape: (1, 56, 56, 64)
ResnetBlock output shape: (1, 28, 28, 128)
ResnetBlock output shape: (1, 14, 14, 256)
ResnetBlock output shape: (1, 7, 7, 512)
GlobalAveragePooling2D output shape: (1, 512)
Dense output shape: (1, 10)
.. raw:: html
.. raw:: html
Eğitim
------
ResNet'i Fashion-MNIST veri kümesi üzerinde eğitiyoruz, tıpkı öncekiler
gibi.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss 0.017, train acc 0.995, test acc 0.906
4813.9 examples/sec on gpu(0)
.. figure:: output_resnet_46beba_99_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss 0.014, train acc 0.996, test acc 0.913
4672.4 examples/sec on cuda:0
.. figure:: output_resnet_46beba_102_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss 0.013, train acc 0.997, test acc 0.924
5152.2 examples/sec on /GPU:0
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
.. figure:: output_resnet_46beba_105_2.svg
.. raw:: html
.. raw:: html
Özet
----
- İç içe işlev sınıfları arzu edilir. Derin sinir ağlarında bir birim
fonksiyonu olarak ek bir katman öğrenmek (bu aşırı bir durum olsa da)
kolaylaştırılmalıdır.
- Artık eşleme, ağırlık katmanındaki parametreleri sıfıra iterek birim
işlevini daha kolay öğrenebilir.
- Artık blokları ile etkili bir derin sinir ağı eğitebiliriz. Girdiler,
katmanlar arasındaki artık bağlantılar üzerinden daha hızlı
yayılabilir.
- ResNet, hem evrişimli hem de sıralı içerikli, müteakip derin sinir
ağlarının tasarımı üzerinde büyük bir etkiye sahip oldu.
Alıştırmalar
------------
1. :numref:`fig_inception` içindeki başlangıç bloğu ile artık blok
arasındaki ana farklılıklar nelerdir? Başlangıç bloğundaki bazı
yolları kaldırırsak, birbirleriyle nasıl ilişkili olurlar?
2. Farklı sürümleri uygulamak için ResNet makalesindeki
:cite:`He.Zhang.Ren.ea.2016` Tablo 1'e bakın.
3. Daha derin ağlar için ResNet, model karmaşıklığını azaltmada bir
“darboğaz” mimarisi sunar. Uygulamaya çalışın.
4. ResNet'in sonraki sürümlerinde yazarlar “evrişim, toplu
normalleştirme ve etkinleştirme” yapısını “toplu normalleştirme,
etkinleştirme ve evrişim” yapısına değiştirdiler. Bu gelişmeyi
kendiniz uygulayın. Ayrıntılar için
:cite:`He.Zhang.Ren.ea.2016*1`'teki Şekil 1'e bakın.
5. İşlev sınıfları iç içe olsa bile neden işlevlerin karmaşıklığını
sınırsız artıramıyoruz?
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html