Rétropropagation de dégradé via des connexions de saut ResNet

22

Je suis curieux de savoir comment les gradients sont propagés en retour à travers un réseau de neurones à l'aide de modules ResNet / sauter les connexions. J'ai vu quelques questions sur ResNet (par exemple, un réseau de neurones avec des connexions de couche de saut ), mais celui-ci pose spécifiquement des questions sur la rétropropagation des gradients pendant l'entraînement.

L'architecture de base est ici:

entrez la description de l'image ici

J'ai lu cet article, Étude des réseaux résiduels pour la reconnaissance d'image , et dans la section 2, ils expliquent comment l'un des objectifs de ResNet est de permettre un chemin plus court / plus clair pour que le gradient se propage en retour vers la couche de base.

Quelqu'un peut-il expliquer comment le gradient traverse ce type de réseau? Je ne comprends pas très bien comment l'opération d'ajout, et l'absence d'une couche paramétrée après l'ajout, permet une meilleure propagation du gradient. Cela a-t-il quelque chose à voir avec la façon dont le gradient ne change pas lorsqu'il passe par un opérateur d'ajout et est en quelque sorte redistribué sans multiplication?

De plus, je peux comprendre comment le problème du gradient de fuite est atténué si le gradient n'a pas besoin de traverser les couches de poids, mais s'il n'y a pas de flux de gradient à travers les poids, alors comment sont-ils mis à jour après le passage en arrière?

Simon
la source
Juste une question idiote, pourquoi nous passons x comme connexion de saut et ne calcule pas l'inverse (F (x)) pour obtenir x à la fin.Est-ce la cause de la complexité de calcul?
Yash Kumar Atri
Je n'ai pas compris votre point the gradient doesn't need to flow through the weight layers, pourriez-vous expliquer cela?
Anu

Réponses:

13

Ajouter renvoie le dégradé également aux deux entrées. Vous pouvez vous en convaincre en exécutant ce qui suit dans tensorflow:

import tensorflow as tf

graph = tf.Graph()
with graph.as_default():
    x1_tf = tf.Variable(1.5, name='x1')
    x2_tf = tf.Variable(3.5, name='x2')
    out_tf = x1_tf + x2_tf

    grads_tf = tf.gradients(ys=[out_tf], xs=[x1_tf, x2_tf])
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        fd = {
            out_tf: 10.0
        }
        print(sess.run(grads_tf, feed_dict=fd))

Sortie:

[1.0, 1.0]

Ainsi, le gradient sera:

  • renvoyé aux couches précédentes, inchangé, via la connexion de saut de couche, et aussi
  • passé au bloc avec des poids et utilisé pour mettre à jour ces poids

Edit: il y a une question: "quelle est l'opération au point où la connexion de l'autoroute et le bloc de réseau neuronal se rejoignent à nouveau, au bas de la figure 2?"

La réponse est: ils sont sommés. Vous pouvez le voir dans la formule de la figure 2:

sortieF(X)+X

Ce que cela dit, c'est que:

  • les valeurs dans le bus ( )X
  • sont ajoutés aux résultats du passage des valeurs de bus, , à travers le réseau, c'est-à-direF ( x )XF(X)
  • pour donner la sortie du bloc résiduel, que j'ai étiqueté ici commesortie

Modifier 2:

Réécriture dans des mots légèrement différents:

  • vers l'avant, les données d'entrée descendent sur le bus
    • aux points le long du bus, les blocs résiduels peuvent apprendre à ajouter / supprimer des valeurs au vecteur de bus
  • dans le sens inverse, les gradients redescendent dans le bus
    • en cours de route, les gradients mettent à jour les blocs résiduels qu'ils dépassent
    • les blocs résiduels modifieront eux aussi légèrement les gradients

Les blocs résiduels modifient les gradients qui reculent, mais il n'y a pas de fonctions de «compression» ou «d'activation» par lesquelles les gradients traversent. Les fonctions 'écraser' / 'activation' sont à l'origine du problème de gradient d'explosion / de fuite, donc en supprimant celles du bus lui-même, nous atténuons considérablement ce problème.

Edit 3: Personnellement j'imagine un resnet dans ma tête comme le schéma suivant. C'est topologiquement identique à la figure 2, mais cela montre peut-être plus clairement comment le bus s'écoule directement à travers le réseau, tandis que les blocs résiduels en tapent simplement les valeurs, et ajoutent / suppriment un petit vecteur contre le bus:

entrez la description de l'image ici

Hugh Perkins
la source
1
si le gradient passe également à travers les blocs de poids (comme dans les réseaux réguliers), d'où vient l'avantage de réinitialisation? Bien sûr, cela permet au gradient de passer directement à l'entrée de base, mais comment cela offre-t-il une augmentation des performances lorsque l'autre chemin est toujours formé normalement?
Simon
3
Je vois. Donc, un gradient saute directement vers x, l'autre se propage à travers les poids vers x. sont-ils résumés lorsqu'ils atteignent x parce que x s'est divisé en 2 chemins? si tel est le cas, le dégradé ne change-t-il pas encore lorsqu'il recule dans ces couches?
Simon
1
Les gradients descendent tout le long de la pile, inchangés. Cependant, chaque bloc apporte ses propres changements de gradient dans la pile, après avoir appliqué ses mises à jour de poids et généré son propre ensemble de dégradés. Chaque bloc a à la fois une entrée et une sortie, et les gradients sortiront de l'entrée, pour revenir dans la "route" du gradient.
Hugh Perkins
1
@RonakAgrawal a ajouté une modification montrant la somme de l'opératoin de la figure 2 et l'expliquant
Hugh Perkins
1
a ajouté une deuxième modification reformulant un peu mon explication :)
Hugh Perkins