Gdzie mogę wywołać funkcję BatchNormalization w Keras?


167

Jeśli chcę użyć funkcji BatchNormalization w Keras, czy muszę wywoływać ją tylko raz na początku?

Przeczytałem do tego dokumentację: http://keras.io/layers/normalization/

Nie wiem, gdzie mam to nazywać. Poniżej znajduje się mój kod, który próbuje go użyć:

model = Sequential()
keras.layers.normalization.BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None)
model.add(Dense(64, input_dim=14, init='uniform'))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(64, init='uniform'))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(2, init='uniform'))
model.add(Activation('softmax'))

sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)
model.fit(X_train, y_train, nb_epoch=20, batch_size=16, show_accuracy=True, validation_split=0.2, verbose = 2)

Pytam, ponieważ jeśli uruchomię kod z drugą linią zawierającą normalizację wsadową i jeśli uruchomię kod bez drugiej linii, otrzymam podobne wyniki. Więc albo nie wywołuję funkcji we właściwym miejscu, albo myślę, że nie robi to dużej różnicy.

Odpowiedzi:


225

Aby odpowiedzieć na to pytanie bardziej szczegółowo, i jak powiedział Pavel, normalizacja wsadowa to tylko kolejna warstwa, więc możesz jej użyć jako takiej do stworzenia pożądanej architektury sieci.

Ogólnym przypadkiem użycia jest użycie BN między warstwami liniowymi i nieliniowymi w sieci, ponieważ normalizuje dane wejściowe do funkcji aktywacji, tak że jesteś wyśrodkowany w liniowej sekcji funkcji aktywacji (takiej jak Sigmoid). Jest mała dyskusja o tym tutaj

W powyższym przypadku może to wyglądać następująco:


# import BatchNormalization
from keras.layers.normalization import BatchNormalization

# instantiate model
model = Sequential()

# we can think of this chunk as the input layer
model.add(Dense(64, input_dim=14, init='uniform'))
model.add(BatchNormalization())
model.add(Activation('tanh'))
model.add(Dropout(0.5))

# we can think of this chunk as the hidden layer    
model.add(Dense(64, init='uniform'))
model.add(BatchNormalization())
model.add(Activation('tanh'))
model.add(Dropout(0.5))

# we can think of this chunk as the output layer
model.add(Dense(2, init='uniform'))
model.add(BatchNormalization())
model.add(Activation('softmax'))

# setting up the optimization of our weights 
sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)

# running the fitting
model.fit(X_train, y_train, nb_epoch=20, batch_size=16, show_accuracy=True, validation_split=0.2, verbose = 2)

Mam nadzieję, że to trochę wyjaśnia sprawę.


25
FYI najwyraźniej normalizacja wsadowa działa lepiej w praktyce po funkcji aktywacji
Claudiu

10
Cześć @Claudiu, czy mógłbyś rozwinąć tę informację? Wydaje się, że bezpośrednio zaprzecza powyższej odpowiedzi.
Ben Ogorek

7
@benogorek: jasne, w zasadzie oparłem to całkowicie na wynikach tutaj, gdzie umieszczenie normy wsadowej po relu wypadło lepiej. FWIW Nie udało mi się zastosować tego w taki czy inny sposób w jednej sieci, którą próbowałem
Claudiu

32
Ciekawy. Aby kontynuować, jeśli będziesz czytać dalej w tym podsumowaniu, jest napisane, że ich najlepszy model [GoogLeNet128_BN_lim0606] faktycznie ma warstwę BN PRZED ReLU. Tak więc, podczas gdy BN po aktywacji może poprawić dokładność w odosobnionym przypadku, gdy cały model jest konstruowany, zanim zostanie wykonany najlepiej. Prawdopodobnie jest możliwe, że umieszczenie BN po Aktywacji może poprawić dokładność, ale jest prawdopodobnie zależne od problemu.
Lucas Ramadan

7
@ CarlThomé jakby. Zobacz na przykład ten komentarz reddit autorstwa ReginaldIII. Stwierdzają: „BN normalizuje rozkład cech wychodzących ze splotu, niektóre [z] tych cech mogą być ujemne [i] obcięte przez nieliniowość, taką jak ReLU. Jeśli normalizujesz przed aktywacją, uwzględniasz te ujemne wartości w normalizacja bezpośrednio przed usunięciem ich z przestrzeni cech. BN po aktywacji znormalizuje cechy pozytywne bez statystycznego obciążania ich cechami, które nie przedostają się do następnej warstwy splotu. "
mab

60

Ten wątek jest mylący. Próbowałem skomentować odpowiedź Lucasa Ramadana, ale nie mam jeszcze odpowiednich uprawnień, więc po prostu wstawię to tutaj.

Normalizacja wsadowa działa najlepiej po funkcji aktywacji, a tutaj lub tutaj jest dlaczego: została opracowana, aby zapobiec wewnętrznemu przesunięciu współzmiennej. Wewnętrzne przesunięcie współzmienne występuje, gdy rozkład aktywacjiwarstwy zmienia się znacznie podczas treningu. Normalizacja wsadowa jest stosowana tak, aby dystrybucja danych wejściowych (a te dane wejściowe są dosłownie wynikiem funkcji aktywacji) do określonej warstwy nie zmienia się w czasie z powodu aktualizacji parametrów z każdej partii (lub przynajmniej pozwala na zmianę w korzystny sposób). Wykorzystuje statystykę wsadową do normalizacji, a następnie wykorzystuje parametry normalizacji wsadowej (gamma i beta w oryginalnym artykule) ", aby upewnić się, że transformacja wstawiona do sieci może reprezentować transformację tożsamości" (cytat z oryginalnego artykułu). Ale chodzi o to, że staramy się znormalizować dane wejściowe do warstwy, więc zawsze powinno to następować bezpośrednio przed następną warstwą w sieci. Czy to ”


27
Właśnie zobaczyłem na zajęciach deeplearning.ai, że Andrew Ng mówi, że toczy się debata na ten temat w społeczności Deep Learning. Woli zastosować normalizację wsadową przed nieliniowością.
shahensha

3
@kRazzyR Chodziło mi o to, że prof. Andrew Ng mówił o tym temacie na swoich zajęciach z głębokiego uczenia na deeplearning.ai Powiedział, że społeczność jest podzielona co do właściwego sposobu robienia rzeczy i woli zastosować normalizację wsadową przed zastosowaniem nieliniowości.
shahensha

3
@jmancuso, BN jest stosowane przed aktywacją. Z samego papieru równanie jest g(BN(Wx + b)), gdzie gjest funkcja aktywacji.
yashgarg1232

43

W tym wątku toczy się poważna debata na temat tego, czy BN powinno być stosowane przed nieliniowością bieżącej warstwy, czy też do aktywacji poprzedniej warstwy.

Chociaż nie ma poprawnej odpowiedzi, autorzy Normalizacji wsadowej twierdzą, że należy ją zastosować bezpośrednio przed nieliniowością bieżącej warstwy. Powód (cytat z oryginalnego artykułu) -

„Dodajemy transformatę BN bezpośrednio przed nieliniowością, normalizując x = Wu + b. Mogliśmy również znormalizować dane wejściowe warstwy u, ale ponieważ u jest prawdopodobnie wynikiem innej nieliniowości, kształt jej rozkładu prawdopodobnie zmieni się w trakcie trenowanie, a ograniczenie jego pierwszego i drugiego momentu nie wyeliminowałoby zmiany współzmiennej. W przeciwieństwie do Wu + b jest bardziej prawdopodobne, że będzie miał symetryczny, nierzadki rozkład, czyli „bardziej gaussowski” (Hyv¨arinen & Oja, 2000) ; normalizacja prawdopodobnie spowoduje aktywacje o stabilnym rozkładzie. "


3
Z własnego doświadczenia wynika, że ​​nie robi to dużej różnicy, ale wszystko inne jest równe, zawsze widziałem, że BN działa nieco lepiej, gdy normalizacja wsadowa jest stosowana przed nieliniowością (przed funkcją aktywacji).
Brad Hesse

31

Keras obsługuje teraz tę use_bias=Falseopcję, więc możemy zaoszczędzić trochę obliczeń, pisząc like

model.add(Dense(64, use_bias=False))
model.add(BatchNormalization(axis=bn_axis))
model.add(Activation('tanh'))

lub

model.add(Convolution2D(64, 3, 3, use_bias=False))
model.add(BatchNormalization(axis=bn_axis))
model.add(Activation('relu'))

jak model.add(BatchNormalization())różni się odmodel.add(BatchNormalization(axis=bn_axis))
kRazzy R

@kRazzR nie różni się, jeśli używasz tensorflowjako zaplecza. Jest napisane tutaj, ponieważ skopiował to z keras.applicationsmodułu, gdzie bn_axismusi być określone, aby obsługiwać oba formaty channels_firsti channels_last.
ldavid

9
Czy ktoś może wyjaśnić, jak to się ma do pytania PO? (Jestem raczej początkującym w NN, więc może coś mi brakuje.)
Pepacz 15.04.18

30

Niemal trend stał się teraz, gdy Conv2Dpo a ReLunastępuje po BatchNormalizationwarstwie. Dlatego stworzyłem małą funkcję, aby wywołać je wszystkie naraz. Sprawia, że ​​definicja modelu wygląda na dużo czystszą i łatwiejszą do odczytania.

def Conv2DReluBatchNorm(n_filter, w_filter, h_filter, inputs):
    return BatchNormalization()(Activation(activation='relu')(Convolution2D(n_filter, w_filter, h_filter, border_mode='same')(inputs)))

7
może wepchnij to do keras?
sachinruk

6

Jest to inny rodzaj warstwy, dlatego warto dodać ją jako warstwę w odpowiednim miejscu modelu

model.add(keras.layers.normalization.BatchNormalization())

Zobacz przykład tutaj: https://github.com/fchollet/keras/blob/master/examples/kaggle_otto_nn.py


1
Po dodaniu BatchNormalization wartość val_acc przestała rosnąć w każdej epoce. Wartość val_acc pozostawała na tym samym poziomie po każdej epoce po dodaniu BatchNormalization. Myślałem, że normalizacja wsadowa powinna zwiększyć wartość val_acc. Skąd mam wiedzieć, czy działa prawidłowo? Czy wiesz, co mogło to spowodować?
pr338

niestety link jest już nieaktualny :(
user2324712

Istnieją kopie tego przykładu w widelcach Keras (np. Github.com/WenchenLi/kaggle/blob/master/otto/keras/… ), ale nie wiem, dlaczego został usunięty z oryginalnego repozytorium Keras. kod jest kompatybilny z najnowszymi wersjami Keras.
Pavel Surmenok

4

Normalizacja wsadowa służy do normalizacji warstwy wejściowej oraz warstw ukrytych poprzez dostosowanie średniej i skalowania aktywacji. Ze względu na ten efekt normalizujący z dodatkową warstwą w głębokich sieciach neuronowych, sieć może używać wyższego tempa uczenia się bez zanikania lub eksplozji gradientów. Ponadto normalizacja wsadowa reguluje sieć w taki sposób, że łatwiej jest uogólniać, a zatem nie jest konieczne stosowanie przerywania w celu złagodzenia nadmiernego dopasowania.

Zaraz po obliczeniu funkcji liniowej za pomocą, powiedzmy, Dense () lub Conv2D () w Keras, używamy BatchNormalization (), które oblicza funkcję liniową w warstwie, a następnie dodajemy nieliniowość do warstwy za pomocą Activation ().

from keras.layers.normalization import BatchNormalization
model = Sequential()
model.add(Dense(64, input_dim=14, init='uniform'))
model.add(BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(64, init='uniform'))
model.add(BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(2, init='uniform'))
model.add(BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None))
model.add(Activation('softmax'))

sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)
model.fit(X_train, y_train, nb_epoch=20, batch_size=16, show_accuracy=True, 
validation_split=0.2, verbose = 2)

W jaki sposób stosowana jest normalizacja wsadowa?

Załóżmy, że wprowadziliśmy [l-1] do warstwy l. Mamy również wagi W [l] i jednostkę odchylenia b [l] dla warstwy l. Niech a [l] będzie wektorem aktywacji (tj. Po dodaniu nieliniowości) dla warstwy l i z [l] będzie wektorem przed dodaniem nieliniowości

  1. Używając [l-1] i W [l], możemy obliczyć z [l] dla warstwy l
  2. Zwykle w propagacji z wyprzedzeniem dodajemy jednostkę odchylenia do z [l] na tym etapie, tak jak to z [l] + b [l], ale w normalizacji wsadowej ten krok dodawania b [l] nie jest wymagany i nie używany jest parametr b [l].
  3. Oblicz średnią z [l] i odejmij ją od każdego elementu
  4. Podziel (z [l] - średnia) za pomocą odchylenia standardowego. Nazwij to Z_temp [l]
  5. Teraz zdefiniuj nowe parametry γ i β, które zmienią skalę ukrytej warstwy w następujący sposób:

    z_norm [l] = γ.Z_temp [l] + β

W tym fragmencie kodu Dense () przyjmuje a [l-1], używa W [l] i oblicza z [l]. Następnie natychmiastowa BatchNormalization () wykona powyższe kroki, aby uzyskać z_norm [l]. Następnie natychmiastowa Activation () obliczy tanh (z_norm [l]), aby dać [l] ie

a[l] = tanh(z_norm[l])
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.