.. _sec_dcgan:
Derin Evrişimli Çekişmeli Üretici Ağlar
=======================================
:numref:`sec_basic_gan` içinde, GAN'ların nasıl çalıştığına dair temel
fikirleri tanıttık. Tekdüze veya normal dağılım gibi basit, örneklenmesi
kolay bir dağılımdan örnekler alabileceklerini ve bunları bazı veri
kümelerinin dağılımıyla eşleşiyor gibi görünen örneklere
dönüştürebileceklerini gösterdik. Burada bir 2B Gauss dağılımını
eşleştirme örneğimiz amaca uygunken, özellikle heyecan verici bir örnek
değil.
Bu bölümde, foto-gerçekçi imgeler oluşturmak için GAN'ları nasıl
kullanabileceğinizi göstereceğiz. Modellerimizi derin evrişimli GAN'lara
(DCGAN) dayandıracağız :cite:`Radford.Metz.Chintala.2015`. Ayrımcı
bilgisayarla görme problemleri için çok başarılı olduğu kanıtlanmış
evrişimli mimariyi ödünç alacağız ve GAN'lar aracılığıyla foto-gerçekçi
imgeler oluşturmak için nasıl kullanılabileceklerini göstereceğiz.
.. raw:: html
`__ adresinden elde edilen
Pokemon görselleri koleksiyonudur. İlk önce bu veri kümesini indirelim,
çıkaralım ve yükleyelim.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
#@save
d2l.DATA_HUB['pokemon'] = (d2l.DATA_URL + 'pokemon.zip',
'c065c0e2593b8b161a2d7873e42418bf6a21106c')
data_dir = d2l.download_extract('pokemon')
pokemon = gluon.data.vision.datasets.ImageFolderDataset(data_dir)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
#@save
d2l.DATA_HUB['pokemon'] = (d2l.DATA_URL + 'pokemon.zip',
'c065c0e2593b8b161a2d7873e42418bf6a21106c')
data_dir = d2l.download_extract('pokemon')
pokemon = torchvision.datasets.ImageFolder(data_dir)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
Downloading ../data/pokemon.zip from http://d2l-data.s3-accelerate.amazonaws.com/pokemon.zip...
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
#@save
d2l.DATA_HUB['pokemon'] = (d2l.DATA_URL + 'pokemon.zip',
'c065c0e2593b8b161a2d7873e42418bf6a21106c')
data_dir = d2l.download_extract('pokemon')
batch_size = 256
pokemon = tf.keras.preprocessing.image_dataset_from_directory(
data_dir, batch_size=batch_size, image_size=(64, 64))
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
Downloading ../data/pokemon.zip from http://d2l-data.s3-accelerate.amazonaws.com/pokemon.zip...
Found 40597 files belonging to 721 classes.
.. raw:: html
.. raw:: html
Her bir imgeyi :math:`64 \times 64` şekilde yeniden boyutlandırıyoruz.
``ToTensor`` dönüşümü piksel değerini :math:`[0, 1]` aralığına
izdüşürürken bizim üreticimiz :math:`[- 1, 1]` aralığından çıktılar elde
etmek için tanh işlevini kullanacak. Bu nedenle, verileri değer
aralığına uyması için :math:`0.5` ortalama ve :math:`0.5` standart sapma
ile normalize ediyoruz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
batch_size = 256
transformer = gluon.data.vision.transforms.Compose([
gluon.data.vision.transforms.Resize(64),
gluon.data.vision.transforms.ToTensor(),
gluon.data.vision.transforms.Normalize(0.5, 0.5)
])
data_iter = gluon.data.DataLoader(
pokemon.transform_first(transformer), batch_size=batch_size,
shuffle=True, num_workers=d2l.get_dataloader_workers())
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
batch_size = 256
transformer = torchvision.transforms.Compose([
torchvision.transforms.Resize((64, 64)),
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(0.5, 0.5)
])
pokemon.transform = transformer
data_iter = torch.utils.data.DataLoader(
pokemon, batch_size=batch_size,
shuffle=True, num_workers=d2l.get_dataloader_workers())
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def transform_func(X):
X = X / 255.
X = (X - 0.5) / (0.5)
return X
# For TF>=2.4 use `num_parallel_calls = tf.data.AUTOTUNE`
data_iter = pokemon.map(lambda x, y: (transform_func(x), y),
num_parallel_calls=tf.data.experimental.AUTOTUNE)
data_iter = data_iter.cache().shuffle(buffer_size=1000).prefetch(
buffer_size=tf.data.experimental.AUTOTUNE)
.. raw:: html
.. raw:: html
İlk 20 imgeyi görselleştirelim.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
d2l.set_figsize((4, 4))
for X, y in data_iter:
imgs = X[0:20,:,:,:].transpose(0, 2, 3, 1)/2+0.5
d2l.show_images(imgs, num_rows=4, num_cols=5)
break
.. figure:: output_dcgan_2541de_39_0.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
warnings.filterwarnings('ignore')
d2l.set_figsize((4, 4))
for X, y in data_iter:
imgs = X[0:20,:,:,:].permute(0, 2, 3, 1)/2+0.5
d2l.show_images(imgs, num_rows=4, num_cols=5)
break
.. figure:: output_dcgan_2541de_42_0.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
d2l.set_figsize(figsize=(4, 4))
for X, y in data_iter.take(1):
imgs = X[:20, :, :, :] / 2 + 0.5
d2l.show_images(imgs, num_rows=4, num_cols=5)
.. figure:: output_dcgan_2541de_45_0.svg
.. raw:: html
.. raw:: html
Üretici
-------
Üreticinin, :math:`d`-uzunluklu vektör olan
:math:`\mathbf z \in \mathbb R^d` gürültü değişkenini, genişliği ve
yüksekliği :math:`64 \times 64` olan bir RGB imgesine eşlemesi gerekir.
:numref:`sec_fcn` bölümünde, girdi boyutunu büyütmek için devrik
evrişim katmanı (bkz. :numref:`sec_transposed_conv`) kullanan tam
evrişimli ağı tanıttık. Üreticinin temel bloğu, devrik bir evrişim
katmanı, ardından toptan normalleştirme ve ReLU etkinleştirmesi içerir.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class G_block(nn.Block):
def __init__(self, channels, kernel_size=4,
strides=2, padding=1, **kwargs):
super(G_block, self).__init__(**kwargs)
self.conv2d_trans = nn.Conv2DTranspose(
channels, kernel_size, strides, padding, use_bias=False)
self.batch_norm = nn.BatchNorm()
self.activation = nn.Activation('relu')
def forward(self, X):
return self.activation(self.batch_norm(self.conv2d_trans(X)))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class G_block(nn.Module):
def __init__(self, out_channels, in_channels=3, kernel_size=4, strides=2,
padding=1, **kwargs):
super(G_block, self).__init__(**kwargs)
self.conv2d_trans = nn.ConvTranspose2d(in_channels, out_channels,
kernel_size, strides, padding, bias=False)
self.batch_norm = nn.BatchNorm2d(out_channels)
self.activation = nn.ReLU()
def forward(self, X):
return self.activation(self.batch_norm(self.conv2d_trans(X)))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class G_block(tf.keras.layers.Layer):
def __init__(self, out_channels, kernel_size=4, strides=2, padding="same",
**kwargs):
super().__init__(**kwargs)
self.conv2d_trans = tf.keras.layers.Conv2DTranspose(
out_channels, kernel_size, strides, padding, use_bias=False)
self.batch_norm = tf.keras.layers.BatchNormalization()
self.activation = tf.keras.layers.ReLU()
def call(self, X):
return self.activation(self.batch_norm(self.conv2d_trans(X)))
.. raw:: html
.. raw:: html
Varsayılan olarak, devrik evrişim katmanı :math:`k_h = k_w = 4`'lük
çekirdek, :math:`s_h = s_w = 2`'lik uzun adımlar (stride) ve
:math:`p_h = p_w = 1`'lik dolgu (padding) kullanır.
:math:`n_h^{'} \times n_w^{'} = 16 \times 16` girdi şekli ile, üretici
bloğu girdinin genişliğini ve yüksekliğini iki katına çıkaracaktır.
.. math::
\begin{aligned}
n_h^{'} \times n_w^{'} &= [(n_h k_h - (n_h-1)(k_h-s_h)- 2p_h] \times [(n_w k_w - (n_w-1)(k_w-s_w)- 2p_w]\\
&= [(k_h + s_h (n_h-1)- 2p_h] \times [(k_w + s_w (n_w-1)- 2p_w]\\
&= [(4 + 2 \times (16-1)- 2 \times 1] \times [(4 + 2 \times (16-1)- 2 \times 1]\\
&= 32 \times 32 .\\
\end{aligned}
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = np.zeros((2, 3, 16, 16))
g_blk = G_block(20)
g_blk.initialize()
g_blk(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(2, 20, 32, 32)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = torch.zeros((2, 3, 16, 16))
g_blk = G_block(20)
g_blk(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
torch.Size([2, 20, 32, 32])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = tf.zeros((2, 16, 16, 3)) # Channel last convention
g_blk = G_block(20)
g_blk(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
TensorShape([2, 32, 32, 20])
.. raw:: html
.. raw:: html
Devrik evrişim katmanını :math:`4 \times 4` çekirdek, :math:`1 \times 1`
uzun adımları ve sıfır dolgu olarak değiştirelim. :math:`1 \times 1`
girdi boyutuyla çıktının genişliği ve yüksekliği sırasıyla 3 artar.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = np.zeros((2, 3, 1, 1))
g_blk = G_block(20, strides=1, padding=0)
g_blk.initialize()
g_blk(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(2, 20, 4, 4)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = torch.zeros((2, 3, 1, 1))
g_blk = G_block(20, strides=1, padding=0)
g_blk(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
torch.Size([2, 20, 4, 4])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = tf.zeros((2, 1, 1, 3))
# `padding="valid"` dolgu olmamasına karşılık gelir
g_blk = G_block(20, strides=1, padding="valid")
g_blk(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
TensorShape([2, 4, 4, 20])
.. raw:: html
.. raw:: html
Üretici, girdinin hem genişliğini hem de yüksekliğini 1'den 32'ye
çıkaran dört temel bloktan oluşur. Aynı zamanda, önce saklı değişkeni
:math:`64 \times 8` kanala izdüşürür ve ardından her seferinde kanalları
yarıya indirir. Sonunda, çıktının üretilmesi için bir devrik evrişim
katmanı kullanılır. Genişliği ve yüksekliği istenen :math:`64 \times 64`
şekline uyacak şekilde ikiye katlar ve kanal sayısını :math:`3`'e
düşürür. Çıktı değerlerini :math:`(- 1, 1)` aralığına yansıtmak için
tanh etkinleştirme işlevi uygulanır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
n_G = 64
net_G = nn.Sequential()
net_G.add(G_block(n_G*8, strides=1, padding=0), # Çıktı: (64 * 8, 4, 4)
G_block(n_G*4), # Çıktı: (64 * 4, 8, 8)
G_block(n_G*2), # Çıktı: (64 * 2, 16, 16)
G_block(n_G), # Çıktı: (64, 32, 32)
nn.Conv2DTranspose(
3, kernel_size=4, strides=2, padding=1, use_bias=False,
activation='tanh')) # Çıktı: (3, 64, 64)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
n_G = 64
net_G = nn.Sequential(
G_block(in_channels=100, out_channels=n_G*8,
strides=1, padding=0), # Çıktı: (64 * 8, 4, 4)
G_block(in_channels=n_G*8, out_channels=n_G*4), # Çıktı: (64 * 4, 8, 8)
G_block(in_channels=n_G*4, out_channels=n_G*2), # Çıktı: (64 * 2, 16, 16)
G_block(in_channels=n_G*2, out_channels=n_G), # Çıktı: (64, 32, 32)
nn.ConvTranspose2d(in_channels=n_G, out_channels=3,
kernel_size=4, stride=2, padding=1, bias=False),
nn.Tanh()) # Çıktı: (3, 64, 64)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
n_G = 64
net_G = tf.keras.Sequential([
# Çıktı: (4, 4, 64 * 8)
G_block(out_channels=n_G*8, strides=1, padding="valid"),
G_block(out_channels=n_G*4), # Çıktı: (8, 8, 64 * 4)
G_block(out_channels=n_G*2), # Çıktı: (16, 16, 64 * 2)
G_block(out_channels=n_G), # Çıktı: (32, 32, 64)
# Çıktı: (64, 64, 3)
tf.keras.layers.Conv2DTranspose(
3, kernel_size=4, strides=2, padding="same", use_bias=False,
activation="tanh")
])
.. raw:: html
.. raw:: html
Üreticinin çıktı şeklini doğrulamak için 100 boyutlu bir saklı değişken
oluşturalım.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = np.zeros((1, 100, 1, 1))
net_G.initialize()
net_G(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(1, 3, 64, 64)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = torch.zeros((1, 100, 1, 1))
net_G(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
torch.Size([1, 3, 64, 64])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = tf.zeros((1, 1, 1, 100))
net_G(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
TensorShape([1, 64, 64, 3])
.. raw:: html
.. raw:: html
Ayrımcı
-------
Ayrımcı, etkinleştirme işlevi olarak sızıntılı (leaky) ReLU kullanması
dışında normal bir evrişimli ağdır. :math:`\alpha \in[0, 1]`
verildiğinde, tanım şöyledir:
.. math:: \textrm{sızıntılı ReLU}(x) = \begin{cases}x & \text{eğer}\ x > 0\\ \alpha x &\text{diğer türlü}\end{cases}.
Görüldüğü gibi, :math:`\alpha = 0` ise normal ReLU, :math:`\alpha = 1`
ise bir birim fonksiyondur. :math:`\alpha \in (0, 1)` için, sızıntılı
ReLU, negatif bir girdi için sıfır olmayan bir çıktı veren doğrusal
olmayan bir fonksiyondur. Bir nöronun her zaman negatif bir değer
verebileceği ve bu nedenle ReLU'nun gradyanı 0 olduğu için herhangi bir
ilerleme kaydedemeyeceği "ölmekte olan ReLU" (dying ReLU) problemini
çözmeyi amaçlamaktadır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
alphas = [0, .2, .4, .6, .8, 1]
x = np.arange(-2, 1, 0.1)
Y = [nn.LeakyReLU(alpha)(x).asnumpy() for alpha in alphas]
d2l.plot(x.asnumpy(), Y, 'x', 'y', alphas)
.. figure:: output_dcgan_2541de_111_0.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
alphas = [0, .2, .4, .6, .8, 1]
x = torch.arange(-2, 1, 0.1)
Y = [nn.LeakyReLU(alpha)(x).detach().numpy() for alpha in alphas]
d2l.plot(x.detach().numpy(), Y, 'x', 'y', alphas)
.. figure:: output_dcgan_2541de_114_0.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
alphas = [0, .2, .4, .6, .8, 1]
x = tf.range(-2, 1, 0.1)
Y = [tf.keras.layers.LeakyReLU(alpha)(x).numpy() for alpha in alphas]
d2l.plot(x.numpy(), Y, 'x', 'y', alphas)
.. figure:: output_dcgan_2541de_117_0.svg
.. raw:: html
.. raw:: html
Ayrımcının temel bloğu, bir evrişim katmanı ve ardından bir toptan
normalleştirme katmanı ve bir sızıntılı ReLU etkinleştirmesidir. Evrişim
katmanının hiper parametreleri, üretici bloğundaki devrik evrişim
katmanına benzer.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class D_block(nn.Block):
def __init__(self, channels, kernel_size=4, strides=2,
padding=1, alpha=0.2, **kwargs):
super(D_block, self).__init__(**kwargs)
self.conv2d = nn.Conv2D(
channels, kernel_size, strides, padding, use_bias=False)
self.batch_norm = nn.BatchNorm()
self.activation = nn.LeakyReLU(alpha)
def forward(self, X):
return self.activation(self.batch_norm(self.conv2d(X)))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class D_block(nn.Module):
def __init__(self, out_channels, in_channels=3, kernel_size=4, strides=2,
padding=1, alpha=0.2, **kwargs):
super(D_block, self).__init__(**kwargs)
self.conv2d = nn.Conv2d(in_channels, out_channels, kernel_size,
strides, padding, bias=False)
self.batch_norm = nn.BatchNorm2d(out_channels)
self.activation = nn.LeakyReLU(alpha, inplace=True)
def forward(self, X):
return self.activation(self.batch_norm(self.conv2d(X)))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class D_block(tf.keras.layers.Layer):
def __init__(self, out_channels, kernel_size=4, strides=2, padding="same",
alpha=0.2, **kwargs):
super().__init__(**kwargs)
self.conv2d = tf.keras.layers.Conv2D(out_channels, kernel_size,
strides, padding, use_bias=False)
self.batch_norm = tf.keras.layers.BatchNormalization()
self.activation = tf.keras.layers.LeakyReLU(alpha)
def call(self, X):
return self.activation(self.batch_norm(self.conv2d(X)))
.. raw:: html
.. raw:: html
Varsayılan ayarlara sahip temel bir blok, girdilerin genişliğini ve
yüksekliğini, :numref:`sec_padding` içinde gösterdiğimiz gibi, yarıya
düşürecektir. Örneğin, :math:`k_h = k_w = 4` çekirdek,
:math:`s_h = s_w = 2` uzun adım, :math:`p_h = p_w = 1` dolgu ve
:math:`n_h = n_w = 16` girdi şekli verildiğinde, çıktının şekli şöyle
olacaktır:
.. math::
\begin{aligned}
n_h^{'} \times n_w^{'} &= \lfloor(n_h-k_h+2p_h+s_h)/s_h\rfloor \times \lfloor(n_w-k_w+2p_w+s_w)/s_w\rfloor\\
&= \lfloor(16-4+2\times 1+2)/2\rfloor \times \lfloor(16-4+2\times 1+2)/2\rfloor\\
&= 8 \times 8 .\\
\end{aligned}
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = np.zeros((2, 3, 16, 16))
d_blk = D_block(20)
d_blk.initialize()
d_blk(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(2, 20, 8, 8)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = torch.zeros((2, 3, 16, 16))
d_blk = D_block(20)
d_blk(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
torch.Size([2, 20, 8, 8])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = tf.zeros((2, 16, 16, 3))
d_blk = D_block(20)
d_blk(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
TensorShape([2, 8, 8, 20])
.. raw:: html
.. raw:: html
Ayrımcı üreticinin bir yansımasıdır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
n_D = 64
net_D = nn.Sequential()
net_D.add(D_block(n_D), # Çıktı: (64, 32, 32)
D_block(n_D*2), # Çıktı: (64 * 2, 16, 16)
D_block(n_D*4), # Çıktı: (64 * 4, 8, 8)
D_block(n_D*8), # Çıktı: (64 * 8, 4, 4)
nn.Conv2D(1, kernel_size=4, use_bias=False)) # Çıktı: (1, 1, 1)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
n_D = 64
net_D = nn.Sequential(
D_block(n_D), # Çıktı: (64, 32, 32)
D_block(in_channels=n_D, out_channels=n_D*2), # Çıktı: (64 * 2, 16, 16)
D_block(in_channels=n_D*2, out_channels=n_D*4), # Çıktı: (64 * 4, 8, 8)
D_block(in_channels=n_D*4, out_channels=n_D*8), # Çıktı: (64 * 8, 4, 4)
nn.Conv2d(in_channels=n_D*8, out_channels=1,
kernel_size=4, bias=False)) # Çıktı: (1, 1, 1)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
n_D = 64
net_D = tf.keras.Sequential([
D_block(n_D), # Çıktı: (32, 32, 64)
D_block(out_channels=n_D*2), # Çıktı: (16, 16, 64 * 2)
D_block(out_channels=n_D*4), # Çıktı: (8, 8, 64 * 4)
D_block(out_channels=n_D*8), # Çıktı: (4, 4, 64 * 64)
# Çıktı: (1, 1, 1)
tf.keras.layers.Conv2D(1, kernel_size=4, use_bias=False)
])
.. raw:: html
.. raw:: html
Tek bir tahmin değeri elde etmek için son katmanda çıktı kanalı
:math:`1` olan bir evrişim katmanı kullanır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = np.zeros((1, 3, 64, 64))
net_D.initialize()
net_D(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(1, 1, 1, 1)
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = torch.zeros((1, 3, 64, 64))
net_D(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
torch.Size([1, 1, 1, 1])
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
x = tf.zeros((1, 64, 64, 3))
net_D(x).shape
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
TensorShape([1, 1, 1, 1])
.. raw:: html
.. raw:: html
Eğitim
------
Temel GAN, :numref:`sec_basic_gan`, ile karşılaştırıldığında,
birbirlerine benzer olduklarından hem üretici hem de ayrımcı için aynı
öğrenme oranını kullanıyoruz. Ek olarak, Adam'daki
(:numref:`sec_adam`) :math:`\beta_1`'yı :math:`0.9`'dan :math:`0.5`'e
değiştiriyoruz. Üretici ve ayrımcı birbiriyle çekiştiği için, hızlı
değişen gradyanlarla ilgilenmek için momentumun, ki geçmiş gradyanların
üstel ağırlıklı hareketli ortalamasıdır, düzgünlüğünü azaltır. Ayrıca,
rastgele üretilen ``Z`` gürültüsü bir 4B tensördür ve bu nedenle
hesaplamayı hızlandırmak için GPU kullanırız.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def train(net_D, net_G, data_iter, num_epochs, lr, latent_dim,
device=d2l.try_gpu()):
loss = gluon.loss.SigmoidBCELoss()
net_D.initialize(init=init.Normal(0.02), force_reinit=True, ctx=device)
net_G.initialize(init=init.Normal(0.02), force_reinit=True, ctx=device)
trainer_hp = {'learning_rate': lr, 'beta1': 0.5}
trainer_D = gluon.Trainer(net_D.collect_params(), 'adam', trainer_hp)
trainer_G = gluon.Trainer(net_G.collect_params(), 'adam', trainer_hp)
animator = d2l.Animator(xlabel='epoch', ylabel='loss',
xlim=[1, num_epochs], nrows=2, figsize=(5, 5),
legend=['discriminator', 'generator'])
animator.fig.subplots_adjust(hspace=0.3)
for epoch in range(1, num_epochs + 1):
# Bir dönem eğit
timer = d2l.Timer()
metric = d2l.Accumulator(3) # loss_D, loss_G, num_examples
for X, _ in data_iter:
batch_size = X.shape[0]
Z = np.random.normal(0, 1, size=(batch_size, latent_dim, 1, 1))
X, Z = X.as_in_ctx(device), Z.as_in_ctx(device),
metric.add(d2l.update_D(X, Z, net_D, net_G, loss, trainer_D),
d2l.update_G(Z, net_D, net_G, loss, trainer_G),
batch_size)
# Üretilen örnekleri göster
Z = np.random.normal(0, 1, size=(21, latent_dim, 1, 1), ctx=device)
# Yapay verileri N(0, 1) olarak normalleştirin
fake_x = net_G(Z).transpose(0, 2, 3, 1) / 2 + 0.5
imgs = np.concatenate(
[np.concatenate([fake_x[i * 7 + j] for j in range(7)], axis=1)
for i in range(len(fake_x)//7)], axis=0)
animator.axes[1].cla()
animator.axes[1].imshow(imgs.asnumpy())
# Kayıpları göster
loss_D, loss_G = metric[0] / metric[2], metric[1] / metric[2]
animator.add(epoch, (loss_D, loss_G))
print(f'loss_D {loss_D:.3f}, loss_G {loss_G:.3f}, '
f'{metric[2] / timer.stop():.1f} examples/sec on {str(device)}')
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def train(net_D, net_G, data_iter, num_epochs, lr, latent_dim,
device=d2l.try_gpu()):
loss = nn.BCEWithLogitsLoss(reduction='sum')
for w in net_D.parameters():
nn.init.normal_(w, 0, 0.02)
for w in net_G.parameters():
nn.init.normal_(w, 0, 0.02)
net_D, net_G = net_D.to(device), net_G.to(device)
trainer_hp = {'lr': lr, 'betas': [0.5,0.999]}
trainer_D = torch.optim.Adam(net_D.parameters(), **trainer_hp)
trainer_G = torch.optim.Adam(net_G.parameters(), **trainer_hp)
animator = d2l.Animator(xlabel='epoch', ylabel='loss',
xlim=[1, num_epochs], nrows=2, figsize=(5, 5),
legend=['discriminator', 'generator'])
animator.fig.subplots_adjust(hspace=0.3)
for epoch in range(1, num_epochs + 1):
# Bir dönem eğit
timer = d2l.Timer()
metric = d2l.Accumulator(3) # loss_D, loss_G, num_examples
for X, _ in data_iter:
batch_size = X.shape[0]
Z = torch.normal(0, 1, size=(batch_size, latent_dim, 1, 1))
X, Z = X.to(device), Z.to(device)
metric.add(d2l.update_D(X, Z, net_D, net_G, loss, trainer_D),
d2l.update_G(Z, net_D, net_G, loss, trainer_G),
batch_size)
# Üretilen örnekleri göster
Z = torch.normal(0, 1, size=(21, latent_dim, 1, 1), device=device)
# Yapay verileri N(0, 1) olarak normalleştirin
fake_x = net_G(Z).permute(0, 2, 3, 1) / 2 + 0.5
imgs = torch.cat(
[torch.cat([
fake_x[i * 7 + j].cpu().detach() for j in range(7)], dim=1)
for i in range(len(fake_x)//7)], dim=0)
animator.axes[1].cla()
animator.axes[1].imshow(imgs)
# Kayıpları göster
loss_D, loss_G = metric[0] / metric[2], metric[1] / metric[2]
animator.add(epoch, (loss_D, loss_G))
print(f'loss_D {loss_D:.3f}, loss_G {loss_G:.3f}, '
f'{metric[2] / timer.stop():.1f} examples/sec on {str(device)}')
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def train(net_D, net_G, data_iter, num_epochs, lr, latent_dim,
device=d2l.try_gpu()):
loss = tf.keras.losses.BinaryCrossentropy(
from_logits=True, reduction=tf.keras.losses.Reduction.SUM)
for w in net_D.trainable_variables:
w.assign(tf.random.normal(mean=0, stddev=0.02, shape=w.shape))
for w in net_G.trainable_variables:
w.assign(tf.random.normal(mean=0, stddev=0.02, shape=w.shape))
optimizer_hp = {"lr": lr, "beta_1": 0.5, "beta_2": 0.999}
optimizer_D = tf.keras.optimizers.Adam(**optimizer_hp)
optimizer_G = tf.keras.optimizers.Adam(**optimizer_hp)
animator = d2l.Animator(xlabel='epoch', ylabel='loss',
xlim=[1, num_epochs], nrows=2, figsize=(5, 5),
legend=['discriminator', 'generator'])
animator.fig.subplots_adjust(hspace=0.3)
for epoch in range(1, num_epochs + 1):
# Bir dönem eğit
timer = d2l.Timer()
metric = d2l.Accumulator(3) # loss_D, loss_G, num_examples
for X, _ in data_iter:
batch_size = X.shape[0]
Z = tf.random.normal(mean=0, stddev=1,
shape=(batch_size, 1, 1, latent_dim))
metric.add(d2l.update_D(X, Z, net_D, net_G, loss, optimizer_D),
d2l.update_G(Z, net_D, net_G, loss, optimizer_G),
batch_size)
# Üretilen örnekleri göster
Z = tf.random.normal(mean=0, stddev=1, shape=(21, 1, 1, latent_dim))
# Yapay verileri N(0, 1) olarak normalleştirin
fake_x = net_G(Z) / 2 + 0.5
imgs = tf.concat([tf.concat([fake_x[i * 7 + j] for j in range(7)],
axis=1)
for i in range(len(fake_x) // 7)], axis=0)
animator.axes[1].cla()
animator.axes[1].imshow(imgs)
# Kayıpları göster
loss_D, loss_G = metric[0] / metric[2], metric[1] / metric[2]
animator.add(epoch, (loss_D, loss_G))
print(f'loss_D {loss_D:.3f}, loss_G {loss_G:.3f}, '
f'{metric[2] / timer.stop():.1f} examples/sec on {str(device)}')
.. raw:: html
.. raw:: html
Modeli sadece gösterim amaçlı az sayıda dönemle eğitiyoruz. Daha iyi
performans için, ``num_epochs`` değişkeni daha büyük bir sayıya
ayarlayabiliriz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
latent_dim, lr, num_epochs = 100, 0.005, 20
train(net_D, net_G, data_iter, num_epochs, lr, latent_dim)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss_D 0.039, loss_G 5.855, 2629.3 examples/sec on gpu(0)
.. figure:: output_dcgan_2541de_183_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
latent_dim, lr, num_epochs = 100, 0.005, 20
train(net_D, net_G, data_iter, num_epochs, lr, latent_dim)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss_D 0.022, loss_G 7.624, 1068.4 examples/sec on cuda:0
.. figure:: output_dcgan_2541de_186_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
latent_dim, lr, num_epochs = 100, 0.0005, 40
train(net_D, net_G, data_iter, num_epochs, lr, latent_dim)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss_D 0.371, loss_G 2.248, 2298.3 examples/sec on
.. figure:: output_dcgan_2541de_189_1.svg
.. raw:: html
.. raw:: html
Özet
----
- DCGAN mimarisi, ayrımcı için dört evrişimli katmana ve üretici için
dört "kesirli uzun adımlı" evrişimli katmana sahiptir.
- Ayrımcı, toptan normalleştirme (girdi katmanı hariç) ve sızıntılı
ReLU etkinleştirmeleri olan 4 katman uzun adımlı evrişimlerdir.
- Sızıntılı ReLU, negatif bir girdi için sıfır olmayan bir çıktı veren
doğrusal olmayan bir fonksiyondur. "Ölen ReLU" sorununu çözmeyi
amaçlar ve gradyanların mimari boyunca daha kolay akmasına yardımcı
olur.
Alıştırmalar
------------
1. Sızıntılı ReLU yerine standart ReLU etkinleştirmesi kullanırsak ne
olur?
2. DCGAN'ı Fashion-MNIST'e uygulayın ve hangi kategorinin işe yarayıp
hangilerinin yaramadığını görün.
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html