Diepgaande praktische tips (1) - Gewichtsinitialisatie

Basisprincipes, valkuilen bij gewichtsinitialisatie en best practices

https://pixabay.com/photo-1600668/

Motivatie

Als een beginner in diep leren, realiseerde ik me onder meer dat er niet veel online documentatie is die alle diepgaande leertricks op één plek behandelt. Er zijn veel kleine best practices, variërend van eenvoudige trucs zoals het initialiseren van gewichten, regularisatie tot enigszins complexe technieken zoals cyclische leerpercentages die training en debuggen van neurale netten eenvoudiger en efficiënter kunnen maken. Dit inspireerde me om deze serie blogs te schrijven waar ik zoveel mogelijk nuances zal behandelen om het implementeren van diep leren voor jou eenvoudiger te maken.

Bij het schrijven van deze blog is de veronderstelling dat je een basisidee hebt van hoe neurale netwerken worden getraind. Een goed begrip van gewichten, vooroordelen, verborgen lagen, activeringen en activeringsfuncties zal de inhoud duidelijker maken. Ik zou deze cursus aanbevelen als je een basisbasis voor diep leren wilt bouwen.

Opmerking - Telkens wanneer ik verwijs naar lagen van een neuraal netwerk, impliceert dit de lagen van een eenvoudig neuraal netwerk, d.w.z. de volledig verbonden lagen. Natuurlijk zijn sommige van de methoden waar ik het over heb ook van toepassing op convolutionele en terugkerende neurale netwerken. In deze blog ga ik het hebben over de problemen met betrekking tot de initialisatie van gewichtsmatrices en manieren om ze te verminderen. Laten we daarvoor wat basisbegrippen en notaties behandelen die we in de toekomst zullen gebruiken.

Basisprincipes en notaties

Overweeg een neuraal netwerk met L-lagen, dat L-1 verborgen lagen en 1 uitgangslaag heeft. De parameters (gewichten en voorspanningen) van laag 1 worden weergegeven als

Naast gewichten en vooroordelen worden tijdens het trainingsproces de volgende tussentijdse variabelen berekend

Een neuraal netwerk trainen bestaat uit 4 stappen:

  1. Initialiseer gewichten en vooroordelen.
  2. Voorwaartse voortplanting: Met behulp van de input X, gewichten W en biases b, berekenen we voor elke laag Z en A. In de laatste laag berekenen we f (A ^ (L-1)) die een sigmoïde, softmax of lineaire functie kan zijn van A ^ (L-1) en dit geeft de voorspelling y_hat.
  3. Bereken de verliesfunctie: dit is een functie van het werkelijke label y en het voorspelde label y_hat. Het geeft aan hoe ver onze voorspellingen van het werkelijke doel verwijderd zijn. Ons doel is om deze verliesfunctie te minimaliseren.
  4. Achterwaartse voortplanting: In deze stap berekenen we de gradiënten van de verliesfunctie f (y, y_hat) met betrekking tot A, W en b genaamd dA, dW en db. Met behulp van deze verlopen werken we de waarden van de parameters van de laatste laag naar de eerste bij.
  5. Herhaal stappen 2-4 voor herhalingen / tijdvakken totdat we het gevoel hebben dat we de verliesfunctie hebben geminimaliseerd, zonder de treingegevens te overfitting (meer hierover later!)

Hier is een korte blik op stappen 2, 3 en 4 voor een netwerk met 2 lagen, d.w.z. één verborgen laag. (Merk op dat ik de bias-termen hier niet heb toegevoegd voor de eenvoud):

Voorwaartse voortplantingAchterwaartse voortplanting

Gewichten initialiseren W

Een van de uitgangspunten om voor te zorgen tijdens het opbouwen van uw netwerk, is het correct initialiseren van uw gewichtsmatrix. Laten we 2 scenario's bekijken die problemen kunnen veroorzaken tijdens het trainen van het model:

1. Initialiseren van alle gewichten op 0

Laten we het er maar uitbrengen - dit maakt uw model equivalent aan een lineair model. Wanneer u alle gewicht op 0 instelt, is de afgeleide met betrekking tot de verliesfunctie hetzelfde voor elke w in W ^ l, dus alle gewichten hebben dezelfde waarden in de daaropvolgende iteratie. Dit maakt de verborgen eenheden symmetrisch en gaat door voor alle herhalingen die u uitvoert. Als u gewichten op nul zet, is uw netwerk niet beter dan een lineair model. Het is belangrijk op te merken dat het instellen van biases op 0 geen problemen veroorzaakt omdat niet-gewichten zorgen voor het doorbreken van de symmetrie en zelfs als bias 0 is, zijn de waarden in elk neuron nog steeds anders.

2. Initialiseren van gewichten willekeurig

Gewichten willekeurig initialiseren, volgens standaard normale verdeling (np.random.randn (size_l, size_l-1) in Python) terwijl u met een (diep) netwerk werkt, kan mogelijk tot 2 problemen leiden - verdwijnende verlopen of exploderende verlopen.

a) Verdwijnende verlopen - In het geval van diepe netwerken, voor elke activeringsfunctie, zal abs (dW) kleiner en kleiner worden naarmate we achteruit gaan met elke laag tijdens de propagatie van de rug. De eerdere lagen zijn in zo'n geval het langzaamst te trainen.

De gewichtsupdate is minimaal en resulteert in langzamere convergentie. Dit maakt de optimalisatie van de verliesfunctie langzaam. In het ergste geval kan dit ervoor zorgen dat het neurale netwerk niet meer verder traint.

Meer in het bijzonder, in het geval van sigmoïde (z) en tanh (z), als uw gewichten groot zijn, zal de gradiënt verdwijnend klein zijn, waardoor effectief wordt voorkomen dat de gewichten hun waarde veranderen. Dit komt omdat abs (dW) zeer licht zal toenemen of mogelijk kleiner en kleiner wordt bij elke iteratie. Met RELU (z) zijn verdwijnende verlopen over het algemeen geen probleem, omdat de gradiënt 0 is voor negatieve (en nul) ingangen en 1 voor positieve ingangen.

b) Exploderende verlopen - Dit is precies het tegenovergestelde van verdwijnende verlopen. Overweeg dat je niet-negatieve en grote gewichten en kleine activeringen A hebt (zoals het geval kan zijn voor sigmoïde (z)). Wanneer deze gewichten langs de lagen worden vermenigvuldigd, veroorzaken ze een grote verandering in de kosten. De gradiënten worden dus ook groot. Dit betekent dat de veranderingen in W, door W - ⍺ * dW, in grote stappen zullen zijn, het neerwaartse moment zal toenemen.

Dit kan resulteren in oscilleren rond de minima of zelfs het optimale steeds opnieuw overschrijden en het model zal nooit leren!

Een ander effect van exploderende gradiënten is dat enorme waarden van de gradiënten overflow kunnen veroorzaken, wat resulteert in onjuiste berekeningen of introducties van NaN's. Dit kan ook leiden tot verlies dat de waarde NaN krijgt.

Best practices

1. Het gebruik van RELU / lekkende RELU als de activeringsfunctie, omdat het relatief robuust is voor het probleem met de verdwijnende / exploderende gradiënt (vooral voor netwerken die niet te diep zijn). In het geval van lekkende RELU's hebben ze nooit een gradiënt van 0. Zo sterven ze nooit en gaat de training door.

2. Voor diepe netwerken kunnen we een heuristiek gebruiken om de gewichten te initialiseren, afhankelijk van de niet-lineaire activeringsfunctie. Hier, in plaats van te tekenen vanuit de standaard normale verdeling, tekenen we W uit de normale verdeling met variantie k / n, waarbij k afhankelijk is van de activeringsfunctie. Hoewel deze heuristiek het probleem met de exploderende / verdwijnende gradiënten niet volledig oplost, helpen ze het in grote mate te verminderen. De meest voorkomende zijn:

a) Voor RELU (z) - We vermenigvuldigen de willekeurig gegenereerde waarden van W met:

b) Voor tanh (z) - De heuristiek wordt Xavier-initialisatie genoemd. Het is vergelijkbaar met de vorige, behalve dat k 1 is in plaats van 2.

In TensorFlow W = tf.get_variable ('W', [dims], initializer) waarbij initializer = tf.contrib.layers.xavier_initializer ()

c) Een andere veel gebruikte heuristiek is:

Deze dienen als goede uitgangspunten voor initialisatie en verkleinen de kansen op exploderende of verdwijnende verlopen. Ze stellen de gewichten niet te veel groter dan 1, noch te veel minder dan 1. De gradiënten verdwijnen dus niet of exploderen niet te snel. Ze helpen trage convergentie te voorkomen en zorgen er ook voor dat we niet blijven schommelen van de minima. Er bestaan ​​andere varianten van het bovenstaande, waarbij het hoofddoel opnieuw is om de variantie van de parameters te minimaliseren.

3. Gradiëntknippen - Dit is een andere manier om het exploderende gradiëntprobleem aan te pakken. We stellen een drempelwaarde in en als een gekozen functie van een verloop groter is dan deze drempel, stellen we deze in op een andere waarde. Normaliseer bijvoorbeeld de verlopen wanneer de L2-norm een ​​bepaalde drempel overschrijdt - W = W * drempel / l2_norm (W) als l2_norm (W)> drempel

Een belangrijk punt om op te merken is dat we het hebben gehad over verschillende initialisaties van W, maar niet over de vooroordelen b. Dit komt omdat de gradiënten met betrekking tot voorspanning alleen afhangen van de lineaire activering van die laag, en niet van de gradiënten van de diepere lagen. Er is dus geen afnemende of explosie van gradiënten voor de bias-termen. Zoals eerder vermeld, kunnen ze veilig worden geïnitialiseerd op 0.

Gevolgtrekking

In deze blog hebben we valkuilen bij gewichtsinitialisatie en enkele mitigatietechnieken behandeld. Als ik andere nuttige inzichten over dit onderwerp heb gemist, zou ik het graag van u leren! In de volgende blog zal ik het hebben over regularisatiemethoden om overfitting en gradiëntcontrole te verminderen - een truc om debuggen eenvoudiger te maken!

Referenties

  1. https://www.coursera.org/learn/deep-neural-network/lecture/RwqYe/weight-initialization-for-deep-networks
  2. Neurale netwerken: training met backpropagatie - Jeremy Jordan
  3. Een zachte inleiding tot exploderende gradiënten in neurale netwerken door Jason Brownlee
  4. Verdwijningsverloop probleem
  5. https://www.quora.com/Why-is-it-a-problem-to-have-exploding-gradients-in-a-neural-net-especially-in-an-RNN

Over mij: Afgestudeerd met MS Data Science aan USF en afgestudeerd in Computer Science, heb ik 2 jaar ervaring in het bouwen van voorspellende en aanbeveling-algoritmen en het verkrijgen van zakelijke inzichten voor financiële en particuliere klanten. Ik ben enthousiast over de mogelijkheden om mijn machine learning en diepgaande kennis toe te passen op echte problemen.
Bekijk hier mijn andere blogs!
LinkedIn: https://www.linkedin.com/in/neerja-doshi/