Yoğun Bağlı Ağlar (DenseNet)
============================
ResNet, derin ağlardaki işlevlerin nasıl parametrize edileceği görüşünü
önemli ölçüde değiştirdi. *DenseNet* (yoğun evrişimli ağ) bir dereceye
kadar bunun :cite:`Huang.Liu.Van-Der-Maaten.ea.2017` mantıksal
uzantısıdır. Ona nasıl ulaşacağımızı anlamak için, matematikte küçük bir
gezinti yapalım.
ResNet'ten DenseNet'e
---------------------
Fonksiyonlar için Taylor açılımını hatırlayın. :math:`x = 0` noktası
için şu şekilde yazılabilir:
.. math:: f(x) = f(0) + f'(0) x + \frac{f''(0)}{2!} x^2 + \frac{f'''(0)}{3!} x^3 + \ldots.
Önemli nokta, bir işlevi giderek daha yüksek dereceli terimlerine
ayırmasıdır. Benzer bir davranış ile, ResNet fonksiyonları aşağıdaki
gibi parçalar
.. math:: f(\mathbf{x}) = \mathbf{x} + g(\mathbf{x}).
Yani, ResNet :math:`f`'i basit bir doğrusal terime ve daha karmaşık
doğrusal olmayan bir terime ayırır. İki terimin ötesinde bilgi özümsemek
(zorunlu olarak eklemek değil) istersek ne olur? Bir çözüm DenseNet
:cite:`Huang.Liu.Van-Der-Maaten.ea.2017` oldu.
.. _fig_densenet_block:
.. figure:: ../img/densenet-block.svg
Katmanlar arası bağlantılarda ResNet (sol) ve DenseNet (sağ)
arasındaki temel fark: Toplama ve bitiştirme kullanımı.
:numref:`fig_densenet_block` içinde gösterildiği gibi, ResNet ve
DenseNet arasındaki temel fark, ikinci durumda çıktıların toplanmaktan
ziyade *bitiştirilmesidir* (:math:`[,]` ile gösterilir). Sonuç olarak,
giderek daha karmaşık bir işlev dizisini uyguladıktan sonra
:math:`\mathbf{x}`'ten değerlerine bir eşleme gerçekleştiriyoruz:
.. math::
\mathbf{x} \to \left[
\mathbf{x},
f_1(\mathbf{x}),
f_2([\mathbf{x}, f_1(\mathbf{x})]), f_3([\mathbf{x}, f_1(\mathbf{x}), f_2([\mathbf{x}, f_1(\mathbf{x})])]), \ldots\right].
Sonunda, tüm bu işlevler tekrar öznitelik sayısını azaltmak için MLP'de
birleştirilir. Uygulama açısından bu oldukça basittir: Terimleri
toplamak yerine, bunları bitiştiririz. DenseNet'in adı, değişkenler
arasındaki bağımlılık grafiğinin oldukça yoğunlaştığı gerçeğinden
kaynaklanmaktadır. Böyle bir zincirin son katmanı, önceki tüm katmanlara
yoğun bir şekilde bağlanır. Yoğun bağlantılar :numref:`fig_densenet`
içinde gösterilmiştir.
.. _fig_densenet:
.. figure:: ../img/densenet.svg
DenseNet'teki yoğun bağlantılar.
DenseNet oluşturan ana bileşenler *yoğun bloklar* ve *geçiş
katmanları*\ dır. Birincisi, girdilerin ve çıktıların nasıl
bitiştirildiğini tanımlarken, ikincisi kanal sayısını kontrol eder,
böylece çok büyümezlerdir.
Yoğun Bloklar
-------------
DenseNet, ResNet'in değiştirilmiş “toplu normalleştirme, etkinleştirme
ve evrişim” yapısını kullanır (bkz. :numref:`sec_resnet` içindeki
alıştırma). İlk olarak, bu evrişim blok yapısını uyguluyoruz.
.. 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()
def conv_block(num_channels):
blk = nn.Sequential()
blk.add(nn.BatchNorm(),
nn.Activation('relu'),
nn.Conv2D(num_channels, kernel_size=3, padding=1))
return blk
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
import torch
from torch import nn
from d2l import torch as d2l
def conv_block(input_channels, num_channels):
return nn.Sequential(
nn.BatchNorm2d(input_channels), nn.ReLU(),
nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
import tensorflow as tf
from d2l import tensorflow as d2l
class ConvBlock(tf.keras.layers.Layer):
def __init__(self, num_channels):
super(ConvBlock, self).__init__()
self.bn = tf.keras.layers.BatchNormalization()
self.relu = tf.keras.layers.ReLU()
self.conv = tf.keras.layers.Conv2D(
filters=num_channels, kernel_size=(3, 3), padding='same')
self.listLayers = [self.bn, self.relu, self.conv]
def call(self, x):
y = x
for layer in self.listLayers.layers:
y = layer(y)
y = tf.keras.layers.concatenate([x,y], axis=-1)
return y
.. raw:: html
.. raw:: html
*Yoğun blok*, her biri aynı sayıda çıktı kanalı kullanan birden fazla
evrişim bloğundan oluşur. Bununla birlikte, ileri yaymada, kanal
boyutundaki her evrişim bloğunun girdi ve çıktısını bitiştiririz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class DenseBlock(nn.Block):
def __init__(self, num_convs, num_channels, **kwargs):
super().__init__(**kwargs)
self.net = nn.Sequential()
for _ in range(num_convs):
self.net.add(conv_block(num_channels))
def forward(self, X):
for blk in self.net:
Y = blk(X)
# Her bloğun girdi ve çıktısını kanal boyutunda birleştirin
X = np.concatenate((X, Y), axis=1)
return X
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class DenseBlock(nn.Module):
def __init__(self, num_convs, input_channels, num_channels):
super(DenseBlock, self).__init__()
layer = []
for i in range(num_convs):
layer.append(conv_block(
num_channels * i + input_channels, num_channels))
self.net = nn.Sequential(*layer)
def forward(self, X):
for blk in self.net:
Y = blk(X)
# Her bloğun girdi ve çıktısını kanal boyutunda birleştirin
X = torch.cat((X, Y), dim=1)
return X
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class DenseBlock(tf.keras.layers.Layer):
def __init__(self, num_convs, num_channels):
super(DenseBlock, self).__init__()
self.listLayers = []
for _ in range(num_convs):
self.listLayers.append(ConvBlock(num_channels))
def call(self, x):
for layer in self.listLayers.layers:
x = layer(x)
return x
.. raw:: html
.. raw:: html
Aşağıdaki örnekte, 10 çıktı kanalından oluşan 2 evrişim bloğu içeren bir
``DenseBlock`` örneği tanımlıyoruz. 3 kanallı bir girdi kullanırken,
:math:`3+2\times 10=23` kanallı bir çıktı alacağız. Evrişim blok
kanallarının sayısı, girdi kanallarının sayısına göre çıktı kanallarının
sayısındaki büyümeyi kontrol eder. Bu aynı zamanda *büyüme oranı* olarak
da adlandırılır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = DenseBlock(2, 10)
blk.initialize()
X = np.random.uniform(size=(4, 3, 8, 8))
Y = blk(X)
Y.shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(4, 23, 8, 8)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = DenseBlock(2, 3, 10)
X = torch.randn(4, 3, 8, 8)
Y = blk(X)
Y.shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
torch.Size([4, 23, 8, 8])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = DenseBlock(2, 10)
X = tf.random.uniform((4, 8, 8, 3))
Y = blk(X)
Y.shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
TensorShape([4, 8, 8, 23])
.. raw:: html
.. raw:: html
Geçiş Katmanları
----------------
Her yoğun blok kanal sayısını artıracağından, çok fazla sayıda eklemek
aşırı karmaşık bir modele yol açacaktır. Modelin karmaşıklığını kontrol
etmek için bir *geçiş katmanı* kullanılır. :math:`1\times 1` evrişimli
katmanı kullanarak kanal sayısını azaltılır ve ortalama ortaklama
tabakasının yüksekliğini ve genişliğini 2'lik bir uzun adımla yarıya
indirilir ve modelin karmaşıklığını daha da azaltılır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def transition_block(num_channels):
blk = nn.Sequential()
blk.add(nn.BatchNorm(), nn.Activation('relu'),
nn.Conv2D(num_channels, kernel_size=1),
nn.AvgPool2D(pool_size=2, strides=2))
return blk
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def transition_block(input_channels, num_channels):
return nn.Sequential(
nn.BatchNorm2d(input_channels), nn.ReLU(),
nn.Conv2d(input_channels, num_channels, kernel_size=1),
nn.AvgPool2d(kernel_size=2, stride=2))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class TransitionBlock(tf.keras.layers.Layer):
def __init__(self, num_channels, **kwargs):
super(TransitionBlock, self).__init__(**kwargs)
self.batch_norm = tf.keras.layers.BatchNormalization()
self.relu = tf.keras.layers.ReLU()
self.conv = tf.keras.layers.Conv2D(num_channels, kernel_size=1)
self.avg_pool = tf.keras.layers.AvgPool2D(pool_size=2, strides=2)
def call(self, x):
x = self.batch_norm(x)
x = self.relu(x)
x = self.conv(x)
return self.avg_pool(x)
.. raw:: html
.. raw:: html
Önceki örnekte yoğun bloğun çıktısına 10 kanallı bir geçiş katmanı
uygulayın. Bu, çıktı kanallarının sayısını 10'a düşürür ve yüksekliği ve
genişliği yarıya indirir.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = transition_block(10)
blk.initialize()
blk(Y).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(4, 10, 4, 4)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = transition_block(23, 10)
blk(Y).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
torch.Size([4, 10, 4, 4])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
blk = TransitionBlock(10)
blk(Y).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
TensorShape([4, 4, 4, 10])
.. raw:: html
.. raw:: html
DenseNet Modeli
---------------
Şimdi, bir DenseNet modeli inşa edeceğiz. DenseNet, ilk olarak ResNet'te
olduğu gibi aynı tek evrişimli katmanı ve maksimum ortaklama katmanını
kullanı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
def block_1():
return tf.keras.Sequential([
tf.keras.layers.Conv2D(64, kernel_size=7, strides=2, padding='same'),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.ReLU(),
tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same')])
.. raw:: html
.. raw:: html
Daha sonra, ResNet'in kullandığı artık bloklardan oluşan dört modüle
benzer, DenseNet de dört yoğun blok kullanır. ResNet'e benzer şekilde,
her yoğun blokta kullanılan evrişimli katmanların sayısını da
ayarlayabiliriz. Burada, :numref:`sec_resnet` içindeki ResNet-18
modeliyle uyumlu olarak katmanların sayısını 4'e ayarladık. Ayrıca,
yoğun bloktaki evrişimli katmanlar için kanal sayısını (yani büyüme
hızı) 32'ye ayarladık, böylece her yoğun bloğa 128 kanal eklenecektir.
ResNet'te, her modül arasındaki yükseklik ve genişlik, 2'lik uzun adımlı
bir artık bloğu ile azaltılır. Burada, yükseklik ve genişliği yarıya
indirip kanal sayısını yarılayarak geçiş katmanını kullanıyoruz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
# `num_channels`: mevcut kanal sayısı
num_channels, growth_rate = 64, 32
num_convs_in_dense_blocks = [4, 4, 4, 4]
for i, num_convs in enumerate(num_convs_in_dense_blocks):
net.add(DenseBlock(num_convs, growth_rate))
# Bu, önceki yoğun bloktaki çıktı kanallarının sayısıdır.
num_channels += num_convs * growth_rate
# Yoğun bloklar arasına kanal sayısını yarıya indiren bir geçiş katmanı
# eklenir
if i != len(num_convs_in_dense_blocks) - 1:
num_channels //= 2
net.add(transition_block(num_channels))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
# `num_channels`: mevcut kanal sayısı
num_channels, growth_rate = 64, 32
num_convs_in_dense_blocks = [4, 4, 4, 4]
blks = []
for i, num_convs in enumerate(num_convs_in_dense_blocks):
blks.append(DenseBlock(num_convs, num_channels, growth_rate))
# Bu, önceki yoğun bloktaki çıktı kanallarının sayısıdır.
num_channels += num_convs * growth_rate
# Yoğun bloklar arasına kanal sayısını yarıya indiren bir geçiş katmanı
# eklenir
if i != len(num_convs_in_dense_blocks) - 1:
blks.append(transition_block(num_channels, num_channels // 2))
num_channels = num_channels // 2
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def block_2():
net = block_1()
# `num_channels`: mevcut kanal sayısı
num_channels, growth_rate = 64, 32
num_convs_in_dense_blocks = [4, 4, 4, 4]
for i, num_convs in enumerate(num_convs_in_dense_blocks):
net.add(DenseBlock(num_convs, growth_rate))
# Bu, önceki yoğun bloktaki çıktı kanallarının sayısıdır.
num_channels += num_convs * growth_rate
# Yoğun bloklar arasına kanal sayısını yarıya indiren bir geçiş katmanı eklenir
if i != len(num_convs_in_dense_blocks) - 1:
num_channels //= 2
net.add(TransitionBlock(num_channels))
return net
.. raw:: html
.. raw:: html
ResNet'e benzer şekilde, çıktıyı üretmek için global bir ortaklama
katmanı ile tam bağlı bir katman bağlanır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net.add(nn.BatchNorm(),
nn.Activation('relu'),
nn.GlobalAvgPool2D(),
nn.Dense(10))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = nn.Sequential(
b1, *blks,
nn.BatchNorm2d(num_channels), nn.ReLU(),
nn.AdaptiveAvgPool2d((1, 1)),
nn.Flatten(),
nn.Linear(num_channels, 10))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def net():
net = block_2()
net.add(tf.keras.layers.BatchNormalization())
net.add(tf.keras.layers.ReLU())
net.add(tf.keras.layers.GlobalAvgPool2D())
net.add(tf.keras.layers.Flatten())
net.add(tf.keras.layers.Dense(10))
return net
.. raw:: html
.. raw:: html
Eğitim
------
Burada daha derin bir ağ kullandığımızdan, bu bölümde, hesaplamaları
basitleştirmek için girdi yüksekliğini ve genişliğini 224'ten 96'ya
düşüreceğiz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
lr, num_epochs, batch_size = 0.1, 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.146, train acc 0.947, test acc 0.876
5248.8 examples/sec on gpu(0)
.. figure:: output_densenet_e82156_99_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
lr, num_epochs, batch_size = 0.1, 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.143, train acc 0.947, test acc 0.903
5535.7 examples/sec on cuda:0
.. figure:: output_densenet_e82156_102_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
lr, num_epochs, batch_size = 0.1, 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.136, train acc 0.950, test acc 0.909
6118.1 examples/sec on /GPU:0
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
.. figure:: output_densenet_e82156_105_2.svg
.. raw:: html
.. raw:: html
Özet
----
- DenseNet, girdilerin ve çıktıların birlikte toplandığı ResNet'in
aksine, çapraz katman bağlantıları bağlanımda, kanal boyutundaki
girdi ve çıktıları bitiştirir.
- DenseNet'i oluşturan ana bileşenler yoğun bloklar ve geçiş
katmanlarıdır.
- Kanal sayısını tekrar küçülten geçiş katmanları ekleyerek ağı
oluştururken boyutsallığı kontrol altında tutmamız gerekir.
Alıştırmalar
------------
1. Neden geçiş katmanında maksimum ortaklama yerine ortalama ortaklama
kullanıyoruz?
2. DenseNet makalesinde belirtilen avantajlardan biri, model
parametrelerinin ResNet'ten daha küçük olmasıdır. Bu neden burada
geçerlidir?
3. DenseNet'in eleştirildiği bir sorun, yüksek bellek tüketimidir.
1. Bu gerçekten doğru mudur? Gerçek GPU bellek tüketimini görmek için
girdi şeklini :math:`224\times 224` olarak değiştirmeye çalışın.
2. Bellek tüketimini azaltmanın alternatif bir yolunu düşünebiliyor
musunuz? Çerçeveyi nasıl değiştirmeniz gerekecektir?
4. DenseNet makalesi :cite:`Huang.Liu.Van-Der-Maaten.ea.2017` Tablo
1'de sunulan çeşitli DenseNet sürümlerini uygulayın.
5. DenseNet fikrini uygulayarak MLP tabanlı bir model tasarlayın.
:numref:`sec_kaggle_house` içindeki konut fiyatı tahmini
çalışmasına uygulayın.
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html