Det underlige tilfælde af den forsvindende og eksploderende gradient

Problemoversigt

Når du træner et neuralt netværk, fører algoritmen hvert træningsinstitut til netværket og beregner output fra hver neuron i hvert på hinanden følgende lag (fremadgående pas). Derefter måler det netværkets outputfejl (forskellen mellem den ønskede output og den aktuelle output af netværket), og det beregner, hvor meget hver neuron i det sidste skjulte lag bidrog til hver outputneurons fejl. Derefter spreder den fejlgradienten tilbage til netværket fra outputlaget til inputlaget (backpropagation). Når algoritmen har beregnet gradienten for omkostningsfunktionen med hensyn til hver parameter i netværket, bruger den gradienterne til at opdatere hver parameter med et gradientafstigningstrin.

Således er gradueringer nøglen til kraften i et neuralt netværk. Gradient bliver dog ofte mindre, når algoritmen skrider frem til de nedre lag. Så lavere lagsvægte er uændrede, hvilket fører til, at træningen aldrig konvergerer til en god løsning. Dette kaldes problemet med forsvindende gradient. Men også det modsatte kan ske, gradienterne kan blive større og større, når en stor fejlgradient ophobes, og så mange lag får sindssygt store vægtopdateringer under træning, hvilket resulterer i, at algoritmen kan afvige. Dette er det eksploderende gradientproblem, som oftest støder på tilbagevendende neurale netværk. Men mere generelt lider dybe neurale netværk af ustabile gradienter.

I denne sammenhæng blev der udgivet et papir for at diskutere vanskelighederne ved at træne et dybt neuralt netværk med titlen ”Forstå vanskeligheden ved at træne dyb feedforward neurale netværk” af 'Xavier Glorot' og 'Yoshua Bengio', hvor de foreslog en måde at afhjælpe dette problem betydeligt af forsvindende / eksploderende gradueringer.

Xavier og He Initialisering

parameterinitialisering er et kritisk aspekt af træningen af ​​et dybt neuralt netværk. Det kan hjælpe træningsprocessen ved at undgå mætning af gradientsignalet (eksploderende gradientproblem) eller overdreven krympning af gradientsignalet (forsvindingsgradientproblem). For at træningen kan flyde ordentligt i retning af fremadrettet retning og bagpropagering, hævder forfatterne, Xavier Glorot og Yoshua Bengio, at vi har brug for, at varianterne i output fra hvert lag er lig med dens input. Denne initialiseringsproces kaldes Xavier-initialisering, hvilket har vist sig at fungere meget godt i praksis.

Generelt er antallet af input- og outputforbindelser for det lag, hvis vægt initialiseres muligvis ikke ens, og som et slags kompromis antyder Glorot og Bengio at bruge deres gennemsnit og foreslår 2 variationer:

Xavier Normal Initializer

udtager prøver fra en normal fordeling med et gennemsnit på 0 og en varians som følger:

  • n-ind og n-ud er antallet af ind- og udgangsforbindelser for det lag, hvis vægt er initialiseret

Xavier Uniform Initializer

udtager prøver fra en ensartet fordeling i intervallet [-a, a]. Parameteren 'a' er defineret som følger:

Xavier-initialisering anbefales til sigmoid- eller tanh-aktiveringsfunktioner.

Papiret Delving Deep in Rectifiers: Overpassing of Human Level Performance on ImageNet Classification by Kaiming He, Xiangyu Zhang, Shaoqing Ren and Jian Sun introducerede en initialiseringsteknik, der er bedre egnet til ReLU-aktiveringsfunktioner kaldet He initialization. Ligesom Xavier-initialiseringen er der to variationer:

Han Normal Initializer

udtager prøver fra en normal fordeling med et gennemsnit på 0 og en varians som følger:

  • ReLU-aktiveringsfunktionen indstiller alle negative værdier til 0. Hvis man antager, at de indgangsindgange fra ReLU-enhederne er centreret omkring 0, producerer halvdelen af ​​dem 0 udgange. Han initialisering kompenserer dette ved at øge variansen to gange sammenlignet med Xavier-initialiseringen.

Han Uniform Initializer

udtager prøver fra en ensartet fordeling i intervallet [-a, a]. Parameteren 'a' er defineret som følger:

Han initialiserer kun inputforbindelserne for det lag, hvis vægt initialiseres, ikke gennemsnittet mellem input og output som ved Xavier-initialisering.

Pytorch-implementering

Se den fulde dokumentation

# Han initialisering
fakkel.nn.init.kaiming_normal_ (tensor, a = 0, mode = 'fan_in', nonlinearity = 'leaky_relu') fakkel.nn.init.kaiming_uniform_ (tensor, a = 0, mode = 'fan_in', nonlinearity = 'leaky_relu' ')
# Xavier initiering
fakkel.nn.init.xavier_uniform_ (tensor, gain = 1.0) fakkel.nn.init.xavier_normal_ (tensor, gain = 1.0)

Ikke-mættede aktiveringsfunktioner

et af perspektiverne i 2010-papiret af Glorot og Bengio var, at de forsvindende / eksploderende gradientproblemer delvis skyldtes et ufuldkommen valg af aktiveringsfunktionen. Og dengang, da dette papir blev offentliggjort, bruger folk sigmoid-aktiveringsfunktion meget mere end nogen anden aktiveringsfunktion. Årsagen bag dette er, at folk blev inspireret af biologiske netværk (menneskelig hjerne), hvor aktiveringsfunktionen normalt er en abstraktion, der repræsenterer hastigheden af ​​handlingspotentiale, der fyrer i cellen. I sin enkleste form er denne funktion binær, det vil sige, enten skyder neuronet eller ikke. Så folk antog, at denne binære / sigmoid aktiveringsfunktion skal være et fremragende valg.

Men det viser sig, at andre aktiveringsfunktioner opfører sig meget bedre i dybe neurale netværk, især ReLU-aktiveringsfunktionen, hovedsageligt fordi den ikke mættes for positive værdier, mens sigmoid mættes for både positive og negative værdier.

sigmoid VS ReLU

Beklageligt, ReLU-aktiveringsfunktionen er ikke ideel Den lider af et problem kaldet døende ReLU. Som en generel sandhed udsender ReLU-neuroner nul og har nulderivater for alle negative input. Så hvis vægtene i netværket altid fører til negative input til en ReLU-neuron, bidrager neuronen faktisk ikke til netværkets træning. Matematisk er gradientbidraget til vægtopdateringerne fra denne neuron altid nul. Dette sker især, når man bruger en stor indlæringshastighed, eller hvis der er en stor negativ bias, på en sådan måde, at neuronen aldrig kommer tilbage til livet og aktiveres på et hvilket som helst datapunkt igen, da gradueringen af ​​ReLU-funktionen er 0, når dens input er negativ.

For at løse dette problem kan det være en god idé at bruge en variant af ReLU-aktiveringsfunktionen, såsom Leaky ReLU (LeakyReLU) eller ELU (eksponentiel lineær enhed)

LeakyReLU

For at løse de døde neuronproblemer blev den utæt ReLU foreslået i 2013 som en aktiveringsfunktion, der introducerer nogle små negative hældninger til ReLU for at opretholde og holde vægtopdateringerne i live under hele udbredelsesprocessen.

LeakyReLU er defineret som Ua (z) = max (az, z)

Hyperparameter alpha (a) definerer, hvor meget funktionen "lækker". Det er skråningen for funktionen for z <0, og den er typisk indstillet til 0,01. LReLU har et identisk resultat i sammenligning med standard ReLU med undtagelse af, at det har ikke-nul-gradienter over hele varigheden takket være alfahældningen. Denne lille hældning sikrer, at Leaky ReLU aldrig dør. De kan gå i en lang koma, men i modsætning til ReLU har de en chance for at vågne op.

ELU (eksponentiel lineær enhed)

I et papir fra 2015 foreslog Djork-Arné Clevert, Thomas Unterthiner og Sepp Hochreiter en ny aktiveringsfunktion kaldet den eksponentielle lineære enhed (ELU), der overgåede alle ReLU-varianter i deres eksperimenter.

I lighed med utæt ReLU har ELU en lille hældning for negative værdier. I stedet for en lige linje bruger den en logkurve som følgende:

ELU er defineret som

ELUα (z) = z hvis z> 0
ELUα (z) = α (exp (z) - 1) hvis z ≤ 0
  • Det påtager sig negative værdier, når z <0, hvilket gør det muligt for enheden at have et gennemsnitligt output tættere på 0 (drej dem ikke til 0, som ReLU gør). Dette hjælper med at løse problemet med forsvindende gradueringer. Hyperparameteret α styrer den værdi, som en ELU mættes til, når z er et stort negativt tal.
  • For z <0 har ELU gradienter, der ikke er nul, hvilket undgår problemet med døende enheder.
  • ELU'er har negative værdier, der skubber middelværdien af ​​aktiveringer tættere på nul. Middelaktiveringer, der er tættere på nul, muliggør hurtigere læring, da de bringer gradienten tættere på den naturlige gradient

ELU er designet til at kombinere de gode dele af ReLU og lækkende ReLU, mens det ikke har det døende ReLU-problem, det mættes for store negative værdier, hvilket tillader dem at være i det væsentlige inaktive. ELU kommer med en ulempe, som er, at ELU er langsommere med at beregne sammenligningen med ReLU og dens varianter (på grund af brugen af ​​den eksponentielle funktion), men som set kompenseres den af ​​den hurtigere konvergenshastighed.

Implementering af Pytorch

LeakyRelu_activ = nn.LeakyReLU (0.1) #fejl negativ_slope = 0.01 elu_activ = nn.ELU () #fejl alfa = 1

Implementering af tensorflow

skjult1 = tf.layers.dense (X, n_hidden1, aktivering = tf.nn.elu, name = "skjult1")

Tensorflow V2 kom med en leakyRelu aktiveringsfunktion, som kan bruges som følger:

tf.nn.leaky_relu (funktioner, alfa = 0,2, navn = Ingen)

Ældre Tensorflow-versioner har ikke en foruddefineret funktion til LeakyReLU, men det er let nok at definere det:

def leaky_relu (z, hældning = 0,01, navn = Ingen): returner tf.maximum (hældning * z, z, name = name)
skjult1 = tf.layers.dense (X, n_hidden1, aktivering = utæt_relu, navn = "skjult1")

Batchnormalisering

Træning af dybe neurale netværk er udfordrende, da de kan være følsomme over for de indledende tilfældige vægte og konfigurationen af ​​indlæringsalgoritmen, mens fordelingen af ​​hvert lags input ændres under træning, når vægterne opdateres. Dette problem er kendt som Internal Covariate Shift. Selvom brug af initialisering af He sammen med en hvilken som helst variant af Relu-aktiveringsfunktion kan reducere problemet med forsvindende / eksploderende gradienter i starten af ​​træningen, men det garanterer ikke, at de ikke kommer tilbage senere i træningen. I denne sammenhæng foreslog Sergey Ioffe og Christian Szegedy en ny teknik kaldet Batch Normalization (BN) for at løse det interne covariate skiftproblem. I deres papir fra 2015 påpegede de, at denne teknik består af at normalisere aktiveringen af ​​hvert lag. Dette skal give hvert lag mulighed for at lære om en mere stabil distribution af input, og som en konsekvens af det fremskynder træning af netværket.

Pytorch-implementering

bn = nn. BatchNorm2d (16)

Find dette 5-lags neurale netværkseksempel (billedstørrelse 28x28):

klasse netværk (nn.Module): def __init __ (self): super (netværk, self) .__ init __ () self.conv1 = nn.Conv2d (in_channels = 1, out_channels = 6, kernel_size = 5, polstring = 2) self. bn1 = nn.BatchNorm2d (6) self.conv2 = nn.Conv2d (in_channels = 6, out_channels = 16, kernel_size = 5) self.bn2 = nn.BatchNorm2d (16) self.pool = nn.MaxPool2d (kernel_size = 2, skridtlængde = 2)
        self.fc1 = nn.Linear (in_features = 16 * 5 * 5, out_features = 128) self.fc2 = nn.Linear (in_features = 128, out_features = 64) self.out = nn.Linear (in_features = 64, out_features = 64) 10)
    def frem (self, x): x = self.pool (F.elu (self.bn1 (self.conv1 (x)))) x = self.pool (F.elu (self.bn2 (self.conv2 (x)) )))) x = x.view (-1, 16 * 5 * 5) x = F.elu (self.fc1 (x)) x = F.elu (self.fc2 (x)) x = self.out (x) returnere x

Implementering af tensorflow

Tensorflow giver funktionen tf.nn.batch_normalization (), der centrerer og normaliserer inputene, men vi skal selv beregne middel- og standardafvigelsen og videregive dem som parametre til denne funktion. Som vi kan se, er det ikke den bedste metode til at fortsætte, i stedet kan vi bruge funktionen tf.keras.layers.BatchNormalization (), der håndterer alle disse beregninger for os.

tf.keras.layers.BatchNormalization

Det er usandsynligt, at batchnormalisering vil have en meget positiv indflydelse på små netværk som 5-lags netværket i det sidste eksempel, men for dybere netværk kan det gøre en enorm forskel.

Gradientklipning

En populær teknik til at løse problemet med eksploderende gradienter er simpelthen at klipse gradienterne under backpropagation, så de aldrig overskrider en tærskel (Det bruges mest til tilbagevendende neurale netværk). Dette kaldes gradientklipning. Generelt foretrækker folk nu Batch Normalization. BN blev ikke designet specielt til at forhindre eksploderende gradienter. BN-lag styrer normen for aktiveringer på det fremadgående pas, og derefter bruger det den samme skalering til bagudgående pas. Men det hjælper ikke med at kontrollere gradueringsnormen. Derfor er det stadig nyttigt at vide om gradientklipning.

Som forklaret kan eksploderende gradienter opstå, når gradienten bliver for stor, og fejlgradienter akkumuleres, hvilket resulterer i et ustabilt netværk. Målet med gradientklipning er at omskalere gradueringer, så deres norm er til en bestemt værdi. Med gradientklipning indføres en forudbestemt gradienttærskel, og gradientnormerne, der overskrider denne tærskel, skaleres ned for at matche normen.

Pytorch-implementering

clipping_value = 1 # vilkårligt antal, du vælger fakkel.nn.utils.clip_grad_norm (model.parameters (), clipping_value)

Implementering af tensorflow

clipping_value = 1 # vilkårligt antal vælger du optimizer = tf.train.GradientDescentOptimizer (learning_rate) grads_vars = optimizer.compute_gradients (loss) clipped = [tf.clip_by_value (grad, -clipping_value, clipping_value), var) for grad, var i grads_vars ] grad_optimizer = optimizer.apply_gradients (klippet)

Clipping_value er et hyperparameter, som vi kan indstille.

Følg mig på LinkedIn.

Du kan også kontakte mig via [email protected]