.. _sec_batch_norm:
Toplu Normalleştirme
====================
Derin sinir ağlarını eğitmek zordur. Üstelik makul bir süre içinde
yakınsamalarını sağlamak çetrefilli olabilir. Bu bölümde, derin ağların
:cite:`Ioffe.Szegedy.2015` yakınsamasını sürekli olarak hızlandıran
popüler ve etkili bir teknik olan *toplu normalleştirme*\ yi
tanıtıyoruz. Daha sonra :numref:`sec_resnet` içinde kapsanan artık
bloklarla birlikte toplu normalleştirme, uygulayıcıların 100'den fazla
katmanlı ağları rutin olarak eğitmelerini mümkün kılmıştır.
Derin Ağları Eğitme
-------------------
Toplu normalleştirmeyi anlamak için, özellikle makine öğrenmesi
modellerini ve sinir ağlarını eğitirken ortaya çıkan birkaç pratik
zorluğu gözden geçirelim.
İlk olarak, veri ön işleme ile ilgili seçimler genellikle nihai
sonuçlarda muazzam bir fark yaratmaktadır. Ev fiyatlarını tahmin etmek
için MLP'i uygulamamızı hatırlayın (:numref:`sec_kaggle_house`).
Gerçek verilerle çalışırken ilk adımımız, girdi özniteliklerimizin her
birinin sıfır ortalaması ve bir varyansı olması için
standartlaştırmaktı. Sezgisel olarak, bu standartleştirme
eniyileyicilerimizle uyumlu olur, çünkü parametreleri benzer ölçekte bir
*önsel dağılıma* yerleştirir.
İkincisi, tipik bir MLP veya CNN için, eğittiğimiz gibi, ara
katmanlardaki değişkenler (örneğin, MLP'deki afin dönüşüm çıktıları),
geniş çapta değişen büyüklüklere sahip değerler alabilir: Model
parametrelerinde hem girdiden çıktıya kadar olan katmanlar boyunca, hem
de aynı katmandaki birimler arasında ve zaman içinde yaptığımız
güncellemeler nedeniyle. Toplu normalleştirmenin mucitleri, bu tür
değişkenlerin dağılımındaki bu kaymanın ağın yakınsamasını
engelleyebileceğini gayri resmi olarak önerdi. Sezgisel olarak, bir
katmanın değişken değerleri başka bir katmandakilerin 100 katı kadarsa,
bunun öğrenme oranlarında telafi edici ayarlamaları gerektirebileceğini
varsayıyoruz.
Üçüncü olarak, daha derin ağlar karmaşıktır ve aşırı öğrenmeye
yatkındır. Bu, düzenlileştirmenin daha kritik hale geldiği anlamına
gelir.
Toplu normalleştirme bireysel katmanlara uygulanır (isteğe bağlı olarak,
hepsi için) ve şu şekilde çalışır: Her eğitim yinelemesinde, ilk olarak,
ortalamalarını çıkararak ve standart sapmaları ile bölerek (toplu
normalleştirme), ki her ikisi de mevcut minigrup istatistiklerinden
tahmin edilir, girdileri normalleştiririz. Daha sonra, bir ölçek
katsayısı ve bir ölçek ofseti (sabit kaydırma değeri) uyguluyoruz. Tam
olarak, *toplu normalleştirme* adı *toplu* istatistiklerine dayanan bu
*normalleştirme*\ den kaynaklanmaktadır.
Boyutu 1 olan minigruplarla toplu normalleştirmeyi uygulamaya
çalışırsak, hiçbir şey öğrenemeyeceğimizi unutmayın. Bunun nedeni,
ortalamayı çıkardıktan sonra, her gizli birimin 0 değerini alacak
olmasıdır! Tahmin edebileceğiniz gibi, ki bu nedenle toplu
normalleştirmeye koca bir bölümü ayırıyoruz, yeterince büyük
minigruplarla, yaklaşım etkili ve istikrarlı olduğunu kanıtlıyor.
Buradan alınması gereken esas, toplu normalleştirmeyi uygularken, grup
boyutunun seçiminin toplu normalleştirmesiz durumda olduğundan daha
önemli olabileceğidir.
Biçimsel olarak, bir minigrup olan :math:`\mathcal{B}`'ye dahil olan
:math:`\mathbf {x} \in \mathcal {B}`, toplu normalleştirmeye
(:math:`\mathrm{BN}`) girdi olursa, toplu normalleştirme
:math:`\mathbf{x}` aşağıdaki ifadeye dönüşür:
.. math:: \mathrm{BN}(\mathbf{x}) = \boldsymbol{\gamma} \odot \frac{\mathbf{x} - \hat{\boldsymbol{\mu}}_\mathcal{B}}{\hat{\boldsymbol{\sigma}}_\mathcal{B}} + \boldsymbol{\beta}.
:label: eq_batchnorm
:eq:`eq_batchnorm` denkleminde,
:math:`\hat{\boldsymbol{\mu}}_\mathcal{B}` örneklem ortalaması ve
:math:`\hat{\boldsymbol{\sigma}}_\mathcal{B}` minigrup
:math:`\mathcal{B}`'nın örneklem standart sapmasıdır. Standartlaştırma
uygulandıktan sonra, ortaya çıkan minigrup sıfır ortalama ve birim
varyansa sahiptir. Birim varyans seçimi (diğer bazı sihirli sayılara
karşı) keyfi bir seçim olduğundan, genel olarak eleman-yönlü *ölçek
parametresi* :math:`\boldsymbol{\gamma}`'yı ve *kayma parametresi*
:math:`\boldsymbol{\beta}`'yı dahil ederiz ve onlar :math:`\mathbf{x}`
ile aynı şekle sahiptirler. :math:`\boldsymbol{\gamma}` ve
:math:`\boldsymbol{\beta}`'in diğer model parametreleriyle birlikte
öğrenilmesi gereken parametreler olduğunu unutmayın.
Sonuç olarak, ara katmanlar için değişken büyüklükleri eğitim sırasında
ıraksamaz, çünkü toplu normalleştirme bunları belirli bir ortalama ve
boyuta (:math:`\hat{\boldsymbol{\mu}}_\mathcal{B}` ve
:math:`{\hat{\boldsymbol{\sigma}}_\mathcal{B}}` üzerinden) aktif olarak
ortalar ve yeniden ölçeklendirir. Uygulayıcının sezgi veya bilgeliğinin
bir parçası, toplu normalleştirmenin daha saldırgan öğrenme oranlarına
izin vermesi gibi görünmesidir.
Resmi olarak, :eq:`eq_batchnorm` denklemindeki
:math:`\hat{\boldsymbol{\mu}}_\mathcal{B}` ve
:math:`{\hat{\boldsymbol{\sigma}}_\mathcal{B}}`'yi aşağıdaki gibi
hesaplıyoruz:
.. math::
\begin{aligned} \hat{\boldsymbol{\mu}}_\mathcal{B} &= \frac{1}{|\mathcal{B}|} \sum_{\mathbf{x} \in \mathcal{B}} \mathbf{x},\\
\hat{\boldsymbol{\sigma}}_\mathcal{B}^2 &= \frac{1}{|\mathcal{B}|} \sum_{\mathbf{x} \in \mathcal{B}} (\mathbf{x} - \hat{\boldsymbol{\mu}}_{\mathcal{B}})^2 + \epsilon.\end{aligned}
Deneysel varyans tahmininin kaybolabileceği durumlarda bile sıfıra
bölmeyi denemediğimizden emin olmak için varyans tahminine küçük bir
sabit :math:`\epsilon > 0` eklediğimizi unutmayın.
:math:`\hat{\boldsymbol{\mu}}_\mathcal{B}` ve
:math:`{\hat{\boldsymbol{\sigma}}_\mathcal{B}}` tahminleri, gürültülü
ortalama ve varyans tahminleri kullanarak ölçekleme sorununa karşı
koymaktadır. Bu gürültücülüğün bir sorun olması gerektiğini
düşünebilirsiniz. Anlaşılacağı gibi, bu aslında faydalıdır.
Bunun derin öğrenmede yinelenen bir tema olduğu ortaya çıkıyor. Teorik
olarak henüz iyi ortaya çıkarılamayan nedenlerden dolayı, eniyilemedeki
çeşitli gürültü kaynakları genellikle daha hızlı eğitime ve daha az
aşırı öğrenmeye neden olur: Bu varyasyon bir düzenlileştirme biçimi
olarak kendini gösteriyor. Bazı ön araştırmalarda,
:cite:`Teye.Azizpour.Smith.2018` ve :cite:`Luo.Wang.Shao.ea.2018`,
toplu normalleşmenin özelliklerini sırasıyla Bayesian önselleri ve
cezaları ile ilişkilendiriyor. Özellikle, bu durum :math:`50 \sim 100`
aralığındaki orta boy minigrup boyutları için toplu normalleştirmenin
neden en iyi şekilde çalıştığı bulmacasına biraz ışık tutuyor.
Eğitilmiş bir modeli sabitlerken, ortalama ve varyansı tahmin etmek için
tüm veri kümesini kullanmayı tercih edeceğimizi düşünebilirsiniz. Eğitim
tamamlandıktan sonra, dahil olduğu gruba bağlı olarak neden aynı imgenin
farklı şekilde sınıflandırılmasını isteyelim? Eğitim sırasında,
modelimizi her güncellediğimizde tüm veri örnekleri için ara değişkenler
değiştiği için bu mutlak hesaplama uygulanabilir değildir. Bununla
birlikte, model eğitildikten sonra, her katmanın değişkenlerinin
ortalamalarını ve varyanslarını tüm veri kümesine dayalı olarak
hesaplayabiliriz. Aslında bu, toplu normalleştirme kullanan modeller
için standart bir uygulamadır ve böylece toplu normalleştirme katmanları
*eğitim modu* (minigrup istatistiklerine göre normalleştirme) ve *tahmin
modunda* (veri kümesi istatistiklerine göre normalleştirme) farklı
şekilde çalışır.
Şimdi toplu normalleştirmenin pratikte nasıl çalıştığına bir göz atmaya
hazırız.
Toplu Normalleştirme Katmanları
-------------------------------
Tam bağlı katmanlar ve evrişimli katmanlar için toplu normalleştirme
uygulamaları biraz farklıdır. Her iki durumu aşağıda tartışıyoruz. Toplu
normalleştirme ve diğer katmanlar arasındaki önemli bir farkın olduğunu
hatırlayın; toplu normalleştirme bir seferinde bir minigrup üzerinde
çalıştığı için, diğer katmanlarına ilerlerken daha önce yaptığımız gibi
toplu iş boyutunu göz ardı edemeyiz.
Tam Bağlı Katmanlar
~~~~~~~~~~~~~~~~~~~
Tam bağlı katmanlara toplu normalleştirme uygularken, orijinal makale,
toplu normalleştirmeyi afin dönüşümden sonra ve doğrusal olmayan
etkinleştirme işlevinden önce ekler (daha sonraki uygulamalar
etkinleştirme işlevlerinden hemen sonra toplu normalleştirme
ekleyebilir) :cite:`Ioffe.Szegedy.2015`. :math:`\mathbf{x}` ile tam
bağlı katmana girdisi, :math:`\mathbf{W}\mathbf{x} + \mathbf{b}` afin
dönüşümü (ağırlık parametresi :math:`\mathbf{W}` ve ek girdi parametresi
:math:`\mathbf{b}` ile) ve :math:`\phi` ile etkinleştirme fonksiyonunu
ifade ederse, tam bağlı bir katman çıktısının, :math:`\mathbf{h}`, toplu
normalleştirme etkinleştirilmiş hesaplanmasını aşağıdaki gibi ifade
edebiliriz:
.. math:: \mathbf{h} = \phi(\mathrm{BN}(\mathbf{W}\mathbf{x} + \mathbf{b}) ).
Ortalama ve varyansın dönüşümün uygulandığı *aynı* minigrup üzerinde
hesaplandığını hatırlayın.
Evrişimli Katmanlar
~~~~~~~~~~~~~~~~~~~
Benzer şekilde, evrişimli katmanlarla, evrişimden sonra ve doğrusal
olmayan etkinleştirme işlevinden önce toplu normalleştirme
uygulayabiliriz. Evrişim birden fazla çıktı kanalı olduğunda, bu
kanalların çıktılarının *her biri* için toplu normalleştirme
gerçekleştirmemiz gerekir ve her kanalın kendi ölçek ve kayma
parametreleri vardır, bunların her ikisi de skalerdir. Minigruplarımızın
:math:`m` örnekleri içerdiğini ve her kanal için evrişim çıktısının
:math:`p` yüksekliği ve :math:`q` genişliği olduğunu varsayalım.
Evrişimli katmanlar için, çıktı kanalı başına :math:`m \cdot p \cdot q`
eleman üzerinde toplu normalleştirmeyi aynı anda gerçekleştiriyoruz.
Böylece, ortalama ve varyansı hesaplarken tüm mekansal konumlar üzerinde
değerleri biraraya getiririz ve sonuç olarak her mekansal konumdaki
değeri normalleştirmek için belirli bir kanal içinde aynı ortalama ve
varyansı uygularız.
Tahmin Sırasında Toplu Normalleştirme
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Daha önce de belirttiğimiz gibi, toplu normalleştirme genellikle eğitim
modunda ve tahmin modunda farklı davranır. İlk olarak, her birinin
minigruplardan tahmin edilmesinden kaynaklanan örneklem ortalamasındaki
ve örneklem varyansındaki gürültü, modeli eğittikten sonra artık arzu
edilen birşey değildir. İkincisi, iş başına toplu normalleştirme
istatistiklerini hesaplama lüksüne sahip olmayabiliriz. Örneğin,
modelimizi bir seferde bir öngörü yapmak için uygulamamız gerekebilir.
Genellikle, eğitimden sonra, değişken istatistiklerin kararlı
tahminlerini hesaplamak ve daha sonra bunları tahmin zamanında
sabitlemek için veri kümesinin tamamını kullanırız. Sonuç olarak, toplu
normalleştirme, eğitim sırasında ve test zamanında farklı davranır.
Hattan düşürmenin de bu özelliği sergilediğini hatırlayın.
Sıfırdan Uygulama
-----------------
Aşağıda, tensörlerle sıfırdan bir toplu normalleştirme tabakası
uyguluyoruz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
from d2l import mxnet as d2l
from mxnet import autograd, init, np, npx
from mxnet.gluon import nn
npx.set_np()
def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum):
# Mevcut modun eğitim modu mu yoksa tahmin modu mu olduğunu
# belirlemek için `autograd`'ı kullan
if not autograd.is_training():
# Tahmin modu ise, hareketli ortalama ile elde edilen ortalama
# ve varyansı doğrudan kullan
X_hat = (X - moving_mean) / np.sqrt(moving_var + eps)
else:
assert len(X.shape) in (2, 4)
if len(X.shape) == 2:
# Tam bağlı bir katman kullanırken, öznitelik
# boyutundaki ortalamayı ve varyansı hesapla
mean = X.mean(axis=0)
var = ((X - mean) ** 2).mean(axis=0)
else:
# İki boyutlu bir evrişim katmanı kullanırken, kanal
# boyutundaki (axis=1) ortalamayı ve varyansı hesaplayın.
# Burada, yayın işleminin daha sonra gerçekleştirilebilmesi
# için `X`'in şeklini korumamız gerekiyor.
mean = X.mean(axis=(0, 2, 3), keepdims=True)
var = ((X - mean) ** 2).mean(axis=(0, 2, 3), keepdims=True)
# Eğitim modunda, standardizasyon için mevcut ortalama ve
# varyans kullanılır
X_hat = (X - mean) / np.sqrt(var + eps)
# Hareketli ortalamayı kullanarak ortalamayı ve varyansı güncelle
moving_mean = momentum * moving_mean + (1.0 - momentum) * mean
moving_var = momentum * moving_var + (1.0 - momentum) * var
Y = gamma * X_hat + beta # Ölçek ve kaydırma
return Y, moving_mean, moving_var
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
import torch
from torch import nn
from d2l import torch as d2l
def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum):
# Mevcut modun eğitim modu mu yoksa tahmin modu mu olduğunu
# belirlemek için `is_grad_enabled`'ı kullanın
if not torch.is_grad_enabled():
# Tahmin modu ise, hareketli ortalama ile elde edilen ortalama
# ve varyansı doğrudan kullan
X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps)
else:
assert len(X.shape) in (2, 4)
if len(X.shape) == 2:
# Tam bağlı bir katman kullanırken, öznitelik
# boyutundaki ortalamayı ve varyansı hesapla
mean = X.mean(dim=0)
var = ((X - mean) ** 2).mean(dim=0)
else:
# İki boyutlu bir evrişim katmanı kullanırken, kanal
# boyutundaki (axis=1) ortalamayı ve varyansı hesaplayın.
# Burada, yayın işleminin daha sonra gerçekleştirilebilmesi
# için `X`'in şeklini korumamız gerekiyor.
mean = X.mean(dim=(0, 2, 3), keepdim=True)
var = ((X - mean) ** 2).mean(dim=(0, 2, 3), keepdim=True)
# Eğitim modunda, standardizasyon için mevcut ortalama ve
# varyans kullanılır
X_hat = (X - mean) / torch.sqrt(var + eps)
# Hareketli ortalamayı kullanarak ortalamayı ve varyansı güncelle
moving_mean = momentum * moving_mean + (1.0 - momentum) * mean
moving_var = momentum * moving_var + (1.0 - momentum) * var
Y = gamma * X_hat + beta # Ölçek ve kaydırma
return Y, moving_mean.data, moving_var.data
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
import tensorflow as tf
from d2l import tensorflow as d2l
def batch_norm(X, gamma, beta, moving_mean, moving_var, eps):
# Hareketli varyansın karekökünün tersini eleman-yönlü hesaplayın
inv = tf.cast(tf.math.rsqrt(moving_var + eps), X.dtype)
# Ölçek ve kaydırma
inv *= gamma
Y = X * inv + (beta - moving_mean * inv)
return Y
.. raw:: html
.. raw:: html
Artık uygun bir ``BatchNorm`` katmanı oluşturabiliriz. Katmanımız
``gamma`` ölçeği ve ``beta`` kayması için uygun parametreleri
koruyacaktır, bunların her ikisi de eğitim sırasında güncellenecektir.
Ayrıca, katmanımız modelin tahmini sırasında sonraki kullanım için
ortalamaların ve varyansların hareketli ortalamalarını koruyacaktır.
Algoritmik ayrıntıları bir kenara bırakırsak, katmanın uygulanmasının
altında yatan tasarım desenine dikkat edin. Tipik olarak, matematiği
ayrı bir işlevde tanımlarız, varsayalım ``batch_norm``. Daha sonra bu
işlevselliği, verileri doğru cihazın bağlamına taşıma, gerekli
değişkenleri tahsis etme ve ilkleme, hareketli ortalamaları takip etme
(ortalama ve varyans için) vb. gibi çoğunlukla kayıt tutma konularını
ele alan özel bir katmana kaynaştırıyoruz. Bu model, matematiğin
basmakalıp koddan temiz bir şekilde ayrılmasını sağlar. Ayrıca, kolaylık
sağlamak için burada girdi şeklini otomatik olarak çıkarma konusunda
endişelenmediğimizi, bu nedenle özniteliklerin sayısını belirtmemiz
gerektiğini unutmayın. Kaygılanmayın, derin öğrenme çerçevesindeki üst
düzey toplu normalleştirme API'leri bunu bizim için halledecek; bunu
daha sonra göreceğiz.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class BatchNorm(nn.Block):
# `num_features`: Tam bağlı bir katman için çıktıların sayısı veya
# evrişimli bir katman için çıktı kanallarının sayısı.
# `num_dims`: Tam bağlı katman için 2 ve evrişimli katman için 4
def __init__(self, num_features, num_dims, **kwargs):
super().__init__(**kwargs)
if num_dims == 2:
shape = (1, num_features)
else:
shape = (1, num_features, 1, 1)
# Ölçek parametresi ve kaydırma parametresi (model parametreleri)
# sırasıyla 1 ve 0 olarak ilklenir
self.gamma = self.params.get('gamma', shape=shape, init=init.One())
self.beta = self.params.get('beta', shape=shape, init=init.Zero())
# Model parametresi olmayan değişkenler 0 ve 1 olarak ilklenir.
self.moving_mean = np.zeros(shape)
self.moving_var = np.ones(shape)
def forward(self, X):
# Ana bellekte `X` yoksa, `moving_mean` ve `moving_var`'ı `X`in
# bulunduğu cihaza kopyalayın
if self.moving_mean.ctx != X.ctx:
self.moving_mean = self.moving_mean.copyto(X.ctx)
self.moving_var = self.moving_var.copyto(X.ctx)
# Güncellenen `moving_mean` ve `moving_var`'ı kaydedin
Y, self.moving_mean, self.moving_var = batch_norm(
X, self.gamma.data(), self.beta.data(), self.moving_mean,
self.moving_var, eps=1e-12, momentum=0.9)
return Y
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class BatchNorm(nn.Module):
# `num_features`: Tam bağlı bir katman için çıktıların sayısı veya
# evrişimli bir katman için çıktı kanallarının sayısı.
# `num_dims`: Tam bağlı katman için 2 ve evrişimli katman için 4
def __init__(self, num_features, num_dims):
super().__init__()
if num_dims == 2:
shape = (1, num_features)
else:
shape = (1, num_features, 1, 1)
# Ölçek parametresi ve kaydırma parametresi (model parametreleri)
# sırasıyla 1 ve 0 olarak ilklenir
self.gamma = nn.Parameter(torch.ones(shape))
self.beta = nn.Parameter(torch.zeros(shape))
# Model parametresi olmayan değişkenler 0 ve 1 olarak ilklenir.
self.moving_mean = torch.zeros(shape)
self.moving_var = torch.ones(shape)
def forward(self, X):
# Ana bellekte `X` yoksa, `moving_mean` ve `moving_var`'ı `X`in
# bulunduğu cihaza kopyalayın
if self.moving_mean.device != X.device:
self.moving_mean = self.moving_mean.to(X.device)
self.moving_var = self.moving_var.to(X.device)
# Güncellenen `moving_mean` ve `moving_var`'ı kaydedin
Y, self.moving_mean, self.moving_var = batch_norm(
X, self.gamma, self.beta, self.moving_mean,
self.moving_var, eps=1e-5, momentum=0.9)
return Y
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
class BatchNorm(tf.keras.layers.Layer):
def __init__(self, **kwargs):
super(BatchNorm, self).__init__(**kwargs)
def build(self, input_shape):
weight_shape = [input_shape[-1], ]
# Ölçek parametresi ve kaydırma parametresi (model parametreleri)
# sırasıyla 1 ve 0 olarak ilklenir
self.gamma = self.add_weight(name='gamma', shape=weight_shape,
initializer=tf.initializers.ones, trainable=True)
self.beta = self.add_weight(name='beta', shape=weight_shape,
initializer=tf.initializers.zeros, trainable=True)
# Model parametresi olmayan değişkenler 0 olarak ilklenir.
self.moving_mean = self.add_weight(name='moving_mean',
shape=weight_shape, initializer=tf.initializers.zeros,
trainable=False)
self.moving_variance = self.add_weight(name='moving_variance',
shape=weight_shape, initializer=tf.initializers.ones,
trainable=False)
super(BatchNorm, self).build(input_shape)
def assign_moving_average(self, variable, value):
momentum = 0.9
delta = variable * momentum + value * (1 - momentum)
return variable.assign(delta)
@tf.function
def call(self, inputs, training):
if training:
axes = list(range(len(inputs.shape) - 1))
batch_mean = tf.reduce_mean(inputs, axes, keepdims=True)
batch_variance = tf.reduce_mean(tf.math.squared_difference(
inputs, tf.stop_gradient(batch_mean)), axes, keepdims=True)
batch_mean = tf.squeeze(batch_mean, axes)
batch_variance = tf.squeeze(batch_variance, axes)
mean_update = self.assign_moving_average(
self.moving_mean, batch_mean)
variance_update = self.assign_moving_average(
self.moving_variance, batch_variance)
self.add_update(mean_update)
self.add_update(variance_update)
mean, variance = batch_mean, batch_variance
else:
mean, variance = self.moving_mean, self.moving_variance
output = batch_norm(inputs, moving_mean=mean, moving_var=variance,
beta=self.beta, gamma=self.gamma, eps=1e-5)
return output
.. raw:: html
.. raw:: html
LeNet'te Toplu Normalleştirme Uygulaması
----------------------------------------
Bağlamda ``BatchNorm``'un nasıl uygulanacağını görmek için, aşağıda
geleneksel bir LeNet modeline (:numref:`sec_lenet`) uyguluyoruz. Toplu
normalleştirmenin, evrişimli katmanlardan veya tam bağlı katmanlardan
sonra, ancak karşılık gelen etkinleştirme işlevlerinden önce
uygulandığını hatırlayın.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = nn.Sequential()
net.add(nn.Conv2D(6, kernel_size=5),
BatchNorm(6, num_dims=4),
nn.Activation('sigmoid'),
nn.AvgPool2D(pool_size=2, strides=2),
nn.Conv2D(16, kernel_size=5),
BatchNorm(16, num_dims=4),
nn.Activation('sigmoid'),
nn.AvgPool2D(pool_size=2, strides=2),
nn.Dense(120),
BatchNorm(120, num_dims=2),
nn.Activation('sigmoid'),
nn.Dense(84),
BatchNorm(84, num_dims=2),
nn.Activation('sigmoid'),
nn.Dense(10))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5), BatchNorm(6, num_dims=4), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, kernel_size=5), BatchNorm(16, num_dims=4), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
nn.Linear(16*4*4, 120), BatchNorm(120, num_dims=2), nn.Sigmoid(),
nn.Linear(120, 84), BatchNorm(84, num_dims=2), nn.Sigmoid(),
nn.Linear(84, 10))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
# Sahip olduğumuz CPU/GPU cihazlarını kullanabilmek için model oluşturma
# veya derlemenin `strategy.scope()` içinde olması için bunun
# `d2l.train_ch6`ya geçirilecek bir fonksiyon olması gerektiğini hatırlayın.
def net():
return tf.keras.models.Sequential([
tf.keras.layers.Conv2D(filters=6, kernel_size=5,
input_shape=(28, 28, 1)),
BatchNorm(),
tf.keras.layers.Activation('sigmoid'),
tf.keras.layers.AvgPool2D(pool_size=2, strides=2),
tf.keras.layers.Conv2D(filters=16, kernel_size=5),
BatchNorm(),
tf.keras.layers.Activation('sigmoid'),
tf.keras.layers.AvgPool2D(pool_size=2, strides=2),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(120),
BatchNorm(),
tf.keras.layers.Activation('sigmoid'),
tf.keras.layers.Dense(84),
BatchNorm(),
tf.keras.layers.Activation('sigmoid'),
tf.keras.layers.Dense(10)]
)
.. raw:: html
.. raw:: html
Daha önce olduğu gibi, ağımızı Fashion-MNIST veri kümesi üzerinde
eğiteceğiz. Bu kod, LeNet'i ilk eğittiğimizdeki ile neredeyse aynıdır
(:numref:`sec_lenet`). Temel fark, daha büyük öğrenme oranıdır.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
lr, num_epochs, batch_size = 1.0, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss 0.268, train acc 0.900, test acc 0.848
16270.9 examples/sec on gpu(0)
.. figure:: output_batch-norm_cf033c_39_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
lr, num_epochs, batch_size = 1.0, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss 0.271, train acc 0.899, test acc 0.835
35526.6 examples/sec on cuda:0
.. figure:: output_batch-norm_cf033c_42_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
lr, num_epochs, batch_size = 1.0, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
net = d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss 0.262, train acc 0.903, test acc 0.814
33999.4 examples/sec on /GPU:0
.. figure:: output_batch-norm_cf033c_45_1.svg
.. raw:: html
.. raw:: html
``gamma`` ölçek parametresine ve ilk toplu normalleştirme katmanından
öğrenilen ``beta`` kayma parametresine bir göz atalım.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net[1].gamma.data().reshape(-1,), net[1].beta.data().reshape(-1,)
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(array([2.858126 , 1.5407592, 3.3153756, 3.3177109, 3.9344418, 2.8494725], ctx=gpu(0)),
array([ 2.3804727 , 1.9225048 , -3.3882306 , -0.6975764 , 0.52322704,
1.433832 ], ctx=gpu(0)))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net[1].gamma.reshape((-1,)), net[1].beta.reshape((-1,))
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(tensor([4.3948, 1.8766, 3.0088, 2.3228, 2.5096, 2.3995], device='cuda:0',
grad_fn=),
tensor([ 2.3678, 0.9310, -1.4606, -1.4570, -3.0370, 2.3806], device='cuda:0',
grad_fn=))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
tf.reshape(net.layers[1].gamma, (-1,)), tf.reshape(net.layers[1].beta, (-1,))
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
(,
)
.. raw:: html
.. raw:: html
Kısa Uygulama
-------------
Kendimizi tanımladığımız ``BatchNorm`` sınıfıyla karşılaştırıldığında,
doğrudan derin öğrenme çerçevesinden üst seviye API'lerde tanımlanan
``BatchNorm`` sınıfını kullanabiliriz. Kod, yukarıdaki uygulamamız ile
hemen hemen aynı görünüyor.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = nn.Sequential()
net.add(nn.Conv2D(6, kernel_size=5),
nn.BatchNorm(),
nn.Activation('sigmoid'),
nn.AvgPool2D(pool_size=2, strides=2),
nn.Conv2D(16, kernel_size=5),
nn.BatchNorm(),
nn.Activation('sigmoid'),
nn.AvgPool2D(pool_size=2, strides=2),
nn.Dense(120),
nn.BatchNorm(),
nn.Activation('sigmoid'),
nn.Dense(84),
nn.BatchNorm(),
nn.Activation('sigmoid'),
nn.Dense(10))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5), nn.BatchNorm2d(6), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, kernel_size=5), nn.BatchNorm2d(16), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
nn.Linear(256, 120), nn.BatchNorm1d(120), nn.Sigmoid(),
nn.Linear(120, 84), nn.BatchNorm1d(84), nn.Sigmoid(),
nn.Linear(84, 10))
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
def net():
return tf.keras.models.Sequential([
tf.keras.layers.Conv2D(filters=6, kernel_size=5,
input_shape=(28, 28, 1)),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Activation('sigmoid'),
tf.keras.layers.AvgPool2D(pool_size=2, strides=2),
tf.keras.layers.Conv2D(filters=16, kernel_size=5),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Activation('sigmoid'),
tf.keras.layers.AvgPool2D(pool_size=2, strides=2),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(120),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Activation('sigmoid'),
tf.keras.layers.Dense(84),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Activation('sigmoid'),
tf.keras.layers.Dense(10),
])
.. raw:: html
.. raw:: html
Aşağıda, modelimizi eğitmek için aynı hiper parametreleri kullanıyoruz.
Özel uygulamamız Python tarafından yorumlanılır iken, her zamanki gibi
üst seviye API sürümünün kodu C++ veya CUDA için derlendiğinden, çok
daha hızlı çalıştığını unutmayın.
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss 0.278, train acc 0.896, test acc 0.861
30949.4 examples/sec on gpu(0)
.. figure:: output_batch-norm_cf033c_75_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss 0.271, train acc 0.899, test acc 0.869
60437.2 examples/sec on cuda:0
.. figure:: output_batch-norm_cf033c_78_1.svg
.. raw:: html
.. raw:: html
.. raw:: latex
\diilbookstyleinputcell
.. code:: python
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
loss 0.289, train acc 0.892, test acc 0.858
42867.3 examples/sec on /GPU:0
.. raw:: latex
\diilbookstyleoutputcell
.. parsed-literal::
:class: output
.. figure:: output_batch-norm_cf033c_81_2.svg
.. raw:: html
.. raw:: html
Tartışma
--------
Sezgisel olarak, toplu normalleştirmenin eniyileme alanını daha pürüzsüz
hale getirdiği düşünülmektedir. Bununla birlikte, derin modelleri
eğitirken gözlemlediğimiz olgular için kurgusal sezgiler ve gerçek
açıklamalar arasında ayrım yaparken dikkatli olmalıyız. İlk etapta daha
basit derin sinir ağlarının (MLP'ler ve geleneksel CNN'ler) neden
genelleştirildiğini bile bilmediğimizi hatırlayın. Hattan düşürme ve
ağırlık sönümü ile bile, görünmeyen verilere genelleme kabiliyetleri
geleneksel öğrenme-kuramsal genelleme garantileri ile açıklanamayacak
kadar esnek kalırlar.
Toplu normalleştirmeyi öneren orijinal çalışmada, yazarlar, güçlü ve
kullanışlı bir araç tanıtmanın yanı sıra, neden çalıştığına dair bir
açıklama sundular: *Dahili eşdeğişken kayması*\ nı (internal covariate
shift) azaltmak. Muhtemelen *dahili eşdeğişken kayması* ile yazarlar,
yukarıda ifade edilen sezgiye benzer bir şey ifade ettiler - değişken
değerlerinin dağılımının eğitim boyunca değiştiği düşüncesi. Bununla
birlikte, bu açıklamayla ilgili iki mesele vardı: i) Bu değişim,
*eşdeğişken kayması*\ ndan çok farklıdır, bir yanlış isimlendirmeye yol
açar. ii) Açıklama az belirtilmiş bir sezgi sunar ancak *neden tam
olarak bu teknik çalışır* sorusunu titiz bir açıklama isteyen açık bir
soru olarak bırakır. Bu kitap boyunca, uygulayıcıların derin sinir
ağlarının gelişimine rehberlik etmek için kullandıkları sezgileri
aktarmayı amaçlıyoruz. Bununla birlikte, bu rehber sezgileri yerleşik
bilimsel gerçeklerden ayırmanın önemli olduğuna inanıyoruz. Sonunda, bu
materyale hakim olduğunuzda ve kendi araştırma makalelerinizi yazmaya
başladığınızda, teknik iddialar ve önseziler arasında konumunuzu bulmak
için net olmak isteyeceksiniz.
Toplu normalleştirmenin başarısını takiben, başarısının *dahili
eşdeğişken kayması* açısından açıklanması, teknik yazındaki
tartışmalarda ve makine öğrenmesi araştırmasının nasıl sunulacağı
konusunda daha geniş bir söylemde defalarca ortaya çıkmıştır. 2017
NeurIPS konferansında Zaman Testi Ödülü'nü kabul ederken verdiği
unutulmaz bir konuşmada Ali Rahimi, modern derin öğrenme pratiğini
simyaya benzeten bir argümanda odak noktası olarak *dahili eşdeğişken
kayması*\ nı kullandı. Daha sonra, örnek makine öğrenmesindeki sıkıntılı
eğilimleri özetleyen bir konum kağıdında (position paper) ayrıntılı
olarak yeniden gözden geçirildi :cite:`Lipton.Steinhardt.2018`. Diğer
yazarlar toplu normalleştirmenin başarısı için alternatif açıklamalar
önerdiler, bazıları toplu normalleştirmenin başarısının bazı yönlerden
orijinal makalede :cite:`Santurkar.Tsipras.Ilyas.ea.2018` iddia
edilenlerin tersi olan davranışları sergilemesinden geldiğini iddia
ettiler.
*Dahili eşdeğişken kayması*\ nın, teknik makine öğrenmesi yazınında
benzer şekilde her yıl yapılan belirsiz iddiaların herhangi birinden
daha fazla eleştiriye layık olmadığını not ediyoruz. Muhtemelen, bu
tartışmaların odak noktası olarak tınlamasını (rezonans), hedef
kitledeki geniş tanınabilirliğine borçludur. Toplu normalleştirme,
neredeyse tüm konuşlandırılmış imge sınıflandırıcılarında uygulanan
vazgeçilmez bir yöntem olduğunu kanıtlamıştır, tekniği tanıtan makaleye
on binlerce atıf kazandırmıştır.
Özet
----
- Model eğitimi sırasında, toplu normalleştirme, minigrubun ortalama ve
standart sapmasını kullanarak sinir ağının ara çıktısını sürekli
olarak ayarlar, böylece sinir ağı boyunca her katmandaki ara çıktının
değerleri daha kararlı olur.
- Tam bağlı katmanlar ve evrişimli katmanlar için toplu normalleştirme
yöntemleri biraz farklıdır.
- Bir hattan düşürme katmanı gibi, toplu normalleştirme katmanları da
eğitim modunda ve tahmin modunda farklı hesaplama sonuçlarına
sahiptir.
- Toplu normalleştirmenin, öncelikle düzenlileştirme olmak üzere birçok
yararlı yan etkisi vardır. Öte yandan, orijinal motivasyondaki dahili
eşdeğişken kaymasını azaltma geçerli bir açıklama gibi görünmüyor.
Alıştırmalar
------------
1. Toplu normalleştirmeden önce ek girdi parametresini tam bağlı
katmandan veya evrişimli katmandan kaldırabilir miyiz? Neden?
2. Toplu normalleştirme olan ve olmayan LeNet için öğrenme oranlarını
karşılaştırın.
1. Eğitim ve test doğruluğundaki artışı çizin.
2. Öğrenme oranını ne kadar büyük yapabilirsiniz?
3. Her katmanda toplu normalleştirmeye ihtiyacımız var mı? Deneyle
gözlemleyebilir misiniz?
4. Toplu normalleştirme ile hattan düşürmeyi yer değiştirebilir misiniz?
Davranış nasıl değişir?
5. ``beta`` ve ``gamma`` parametrelerini sabitleyin, sonuçları
gözlemleyin ve analiz edin.
6. Toplu normalleştirmenin diğer uygulamalarını görmek amacıyla üst
seviye API'lerden ``BatchNorm`` için çevrimiçi belgelerini gözden
geçirin.
7. Araştırma fikirleri: Uygulayabileceğiniz diğer normalleştirme
dönüşümleri düşünün? Olasılık integral dönüşümü uygulayabilir
misiniz? Tam kerteli kovaryans tahminine ne dersiniz?
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html
`Tartışmalar `__
.. raw:: html
.. raw:: html