Table Of Contents

Previous topic

Introduction to Gradient-Based Learning

Next topic

Le défi d’entraîner des réseaux de neurones profonds

This Page

Introduction to Multi-Layer Perceptrons (Feedforward Neural Networks)

Multi-Layer Neural Networks

An MLP (for Multi-Layer Perceptron) or multi-layer neural network defines a family of functions. Let us first consider the most classical case of a single hidden layer neural network, mapping a d-vector to an m-vector (e.g. for regression):

g(x) = b + W \tanh(c + V x)

where x is a d-vector (the input), V is an k \times d matrix (called input-to-hidden weights), c is a k-vector (called hidden units offsets or hidden unit biases), b is an m-vector (called output units offset or output units biases), and W is an m \times h matrix (called hidden-to-output weights).

The vector-valued function h(x)=\tanh(c + V x) is called the output of the hidden layer. Note how the output is an affine transformation of the hidden layer, in the above network. A non-linearity may be tacked on to it in some network architectures. The elements of the hidden layer are called hidden units.

The kind of operation computed by the above h(x) can be applied on h(x) itself, but with different parameters (different biases and weights). This would give rise to a feedforward multi-layer network with two hidden layers. More generally, one can build a deep neural network by stacking more such layers. Each of these layers may have a different dimension (k above). A common variant is to have skip connections, i.e., a layer can take as input not only the layer at the previous level but also some of the lower layers.

Most Common Training Criteria and Output Non-Linearities

Let f(x)=r(g(x)) with r representing the output non-linearity function. In supervised learning, the output f(x) can be compared with a target value y through a loss functional L(f,(x,y)). Here are common loss functionals, with the associated output non-linearity:

  • for ordinary (L2) regression: no non-linearity (r(a)=a), squared loss L(f,(x,y))=||f(x)-y||^2 = \sum_i (f_i(x) - y_i)^2.
  • for median (L1) regression: no non-linearity (r(a)=a), absolute value loss L(f,(x,y))=|f(x)-y|_1 = \sum_i |f_i(x) - y_i|.
  • for 2-way probabilistic classification: sigmoid non-linearity (r(a)={\rm sigmoid}(a)=1/(1+e^{-a}, applied element by element), and cross-entropy loss L(f,(x,y))= -y \log f(x) -(1-y)\log(1-f(x)) for y binary. Note that the sigmoid output f(x) is in the (0,1) interval, and corresponds to an estimator of P(y=1|x). The predicted class is 1 if f(x)>\frac{1}{2}.
  • for multiple binary probabilistic classification: each output element is treated as above.
  • for 2-way hard classification with hinge loss: no non-linearity (r(a)=a) and the hinge loss is L(f,(x,y))= \max(0,1 - (2y-1) f(x)) (again for binary y). This is the SVM classifier loss.
  • the above can be generalized to multiple classes by separately considering the binary classifications of each class against the others.
  • multi-way probabilistic classification: softmax non-linearity (r_i(a) = e^{a_i}/\sum_j e^{a_j} with one output per class) with the negative log-likelihood loss L(f,(x,y)) = - \log f_y(x). Note that \sum_i f_i(x)=1 and 0<f_i(x)<1. Note also how this is equivalent to the cross-entropy loss in the 2-class case (the output for the one of the classes is actually redundant).

The Back-Propagation Algorithm

We just apply the recursive gradient computation algorithm seen previously to the graph formed naturally by the MLP, with one node for each input unit, hidden unit and output unit. Note that each parameter (weight or bias) also corresponds to a node, and the final

Let us formalize a notation for MLPs with more than one hidden layer. Let us denote with h_i the output vector of the i-th layer, starting with h_0=x (the input), and finishing with a special output layer h_L which produces the prediction or output of the network.

With tanh units in the hidden layers, we have (in matrix-vector notation):

  • for k = 1 to L-1:
  • h_k = {\rm tanh}(b_k + W_k h_{k-1})

    where b_k is a vector of biases and W_k is a matrix of weights connecting layer k-1 to layer k. The scalar computation associated with a single unit i of layer k is

    h_{ki} = {\rm tanh}(b_{ki} + \sum_j W_{kij} h_{k-1,j})

In the case of a probabilistic classifier, we would then have a softmax output layer, e.g.,

  • p = h_L = {\rm softmax}(b_L + W_L h_{L-1})

where we used p to denote the output because it is a vector indicating a probability distribution over classes. And the loss is

  • L = - \log p_y

where y is the target class, i.e., we want to maximize p_y=P(Y=y|x), an estimator of the conditional probability of class y given input x.

Let us now see how the recursive application of the chain rule in flow graphs is instantiated in this structure. First of all, let us denote

a_k=b_k + W_k h_{k-1}

(for the argument of the non-linearity at each level) and note (from a small derivation) that

\frac{\partial(- \log p_y)}{\partial a_{L,i}} = (p_i - 1_{y=i})

and that

\frac{\partial \tanh(u)}{\partial u} = (1-\tanh(u)^2).

Now let us apply the back-propagation recipe in the corresponding flow graph. Each parameter (each weight and each bias) is a node, each neuron potential a_{ik} and each neuron output h_{ik} is also a node.

  • starting at the output node: \frac{\partial L}{\partial L} = 1

  • then compute the gradient with respect to each pre-softmax sum a_{L,i}: \frac{\partial L}{\partial a_{L,i}} = \frac{\partial L}{\partial L} \frac{\partial L}{\partial a_{L,i}} = (p_i - 1_{y=i})

  • We can now repeat the same recipe for each layer. For k=L down to 1

    • obtain trivially the gradient wrt biases: \frac{\partial L}{\partial b_{k,i}} = \frac{\partial L}{\partial a_{k,i}} \frac{\partial a_{k,i}}{\partial b_{k,i}}=\frac{\partial L}{\partial a_{k,i}}

    • compute the gradient wrt weights: \frac{\partial L}{\partial W_{k,i,j}} = \frac{\partial L}{\partial a_{k,i}}\frac{\partial a_{k,i}}{\partial W_{k,i,j}} = \frac{\partial L}{\partial a_{k,i}} h_{k-1,j}

    • back-propagate the gradient into lower layer, if k>1:

      • \frac{\partial L}{\partial h_{k-1,j}} = \sum_i \frac{\partial L}{\partial a_{k,i}} \frac{\partial a_{k,i}}{\partial h_{k-1,j}} = \sum_i \frac{\partial L}{\partial a_{ki}} W_{k,i,j}
      • \frac{\partial L}{\partial a_{k-1,j}} = \frac{\partial L}{\partial h_{k-1,j}} \frac{\partial h_{k-1,j}}{\partial a_{k-1,j}}=\frac{\partial L}{\partial h_{k-1,j}} (1 - h_{k-1,j}^2)

Logistic Regression

Logistic regression is a special case of the MLP with no hidden layer (the input is directly connected to the output) and the cross-entropy (sigmoid output) or negative log-likelihood (softmax output) loss. It corresponds to a probabilistic linear classifier and the training criterion is convex in terms of the parameters (which garantees that there is only one minimum, which is global).

Training Multi-Layer Neural Networks

Many algorithms have been proposed to train multi-layer neural networks but the most commonly used ones are gradient-based.

Two fundamental issues guide the various strategies employed in training MLPs:

  • training as efficiently as possible, i.e., getting training error down as quickly as possible, avoiding to get stuck in narrow valleys or even local minima of the cost function,
  • controlling capacity so as to achieve the largest capacity avoids overfitting, i.e., to minimize generalization error.

Problème fondamentalement difficile d’optimisation

L’optimisation du critère d’apprentissage dans les réseaux de neurones multi-couches est difficile car il y a de nombreux minima locaux. On peut même démontrer que de trouver les poids optimaux est NP-dur. Cependant on se contente de trouver un bon minimum local, ou même simplement une valeur suffisamment basse du critère. Comme ce qui nous intéresse est la généralisation et non pas l’erreur d’apprentissage (ce qu’on minimise n’est pas ce qu’on voudrait vraiment minimiser), la différence entre “près d’un minimum” et “au minimum” est souvent sans importance. Par ailleurs, comme il n’y a pas de solution analytique au problème de minimisation, on est forcé de faire cette optimisation de manière itérative.

Choix de l’architecture

En principe, une manière d’accélérer la descente de gradient est de faire des choix qui rendent la matrice Hessienne \frac{\partial^2 C}{\partial \theta_i \partial \theta_j} mieux conditionnée. La dérivée second dans une certaine direction indique la courbure de la fonction de coût dans cette direction. Plus la courbure est grande (vallée étroite) et plus petites doivent être les mises à jour des paramètres si on éviter que l’erreur augmente. Plus précisement, le pas de gradient optimal est 1 sur la courbure. On peut voir cela par une simple expansion de Taylor du coût et est derrière le fameux algorithme de Newton pour l’optimisation. Mettons que l’on soit à \theta^k et que l’on veuille choisir \theta^{k+1} pour qu’il soit un minimum:

C(\theta^{k+1}) = C(\theta^k) + (\theta^{k+1} - \theta^k) C'(\theta^k) + 0.5 (\theta^{k+1} - \theta^k)^2 C''(\theta^k)

0 = \frac{\partial C(\theta^{k+1})}{\partial \theta^{k+1}} = C'(\theta^k) + (\theta^{k+1} - \theta^k) C''(\theta^k)

\theta^{k+1} = \theta^k - \frac{C'(\theta^k)}{C''(\theta^k)}

Donc on veut un pas de gradient égal à l’inverse de la dérivée seconde. On peut montrer que le nombre d’itération d’un algorithme de descente de gradient sera proportionnel au ratio de la plus grande à la plus petite valeur propre de la matrice Hessienne (avec une approximation quadratique de la fonction de coût). La raison de base en est que la plus grande valeur propre limite le pas de gradient maximum (on ne peut aller plus vite que la courbure la plus forte parmi toutes les directions possibles, sinon l’erreur remonte), mais qu’en utilisant le même pas de gradient dans toutes les directions, la convergence sera la plus longue dans la direction la plus “plate” (valeur propre la plus petite).

  • En théorie une couche cachée suffit, mais cette théorie ne dit pas que cette représentation de la fonction sera efficace. En pratique cela a été le choix le plus commun avant 2006, sauf pour les réseaux de neurones à convolution (qui peuvent avoir 5 ou 6 couches par exemple). Parfois on obtient de bien meilleurs résultats avec 2 couches cachées. En fait on peut obtenir une bien meilleure généralisation avec encore plus de couches, mais une initialisation aléatoire ne fonctionne pas bien avec plus de deux couches (mais voir les travaux depuis 2006 sur l’initialisation non-supervisée vorace pour les architectures profondes).
  • Pour la régression ou avec des cibles réelles et non-bornées en général, il vaut généralement mieux utiliser des neurones linéaires à la couche de sortie. Pour la classification, il vaut généralement mieux utiliser des neurones avec non-linéralité (sigmoide ou softmax) à la couche de sortie.
  • Dans certains cas une connexion directe entre l’entrée et la sortie peut être utile. Dans le cas de la régression, elle peut aussi être initialisée directement par le résultat d’une régression linéaire des sorties sur les entrées. Les neurones cachées servent alors seulement à apprendre la partie non-linéaire manquante.
  • Une architecture avec poids partagés, ou bien le partage de certains éléments de l’architecture (e.g., la première couche) entre les réseaux associés à plusieurs tâches connexes, peuvent significativement améliorer la généralisation. Voir aussi la discussion à venir sur les réseaux à convolution.
  • Il vaut mieux utiliser utiliser une non-linéarité symétrique dans les couches cachées (comme la tanh, et non pas la sigmoide), afin d’améliorer le conditionnement du Hessien et éviter la saturation des couches cachées.

Normalisation des entrées

Il est impératif que les entrées soient de moyenne pas trop loin de zéro et de variance pas trop loin de 1. Les valeurs en entrées devraient aussi ne pas avoir une magnitude trop grande. On peut faire certaines transformations monotones non-linéaires qui réduisent les grandes valeurs. Si on a une entrée très grande, elle fait saturer plusieurs neurones et bloque l’apprentissage pour cet exemple. Les magnitudes (variances) des entrées de chaque couche devraient aussi être du même ordre quand on utilise un pas de gradient commun pour toute les couches, pour éviter que l’une des couches devienne le goulot d’étranglement (plus lent à entraîner). En fait, dans le cas linéaire, le conditionnement du Hessien est optimal quand les entrées sont normalisées (donc avec matrice de covariance = identité), ce qui peut se faire en les projetant dans l’espace des vecteurs propres de la matrice X' X, où X est la matrice de dimension nombre d’exemples par nombre d’entrées.

Traitement des sorties désirées

Dans le cas d’apprentissage par minimisation d’un coût quadratique, on doit s’assurer que les sorties désirées

  • sont toujours dans l’intervalle des valeurs que la non-linéarité de la couche de sortie peut produire (et sont à peu près normales N(0,1) dans le cas linéaire),

  • ne sont pas trop proches des valeurs limites de la non-linéarité de la couche de sortie: pour la classification, une valeur optimale est près d’un des deux points d’inflexion (i.e., les points de courbure (dérivée seconde) maximale, environ -0.6 et 0.6 pour tanh, 0.2 et 0.8 pour la sigmoide).

  • Il vaut mieux utiliser le critère d’entropie croisée (ou la vraisemblance conditionnelle) pour la classification probabiliste, ou bien le critère de marge “hinge” (comme pour le perceptron et les SVMs, mais en pénalisant les écarts à la surface de décision au-delà d’une marge). Dans le cas multiclasse, ça donne

    x_+ = x 1_{x>0} est la partie positive et f_i(x) est la sortie (sans non-linéarité) pour la classe i.

Codage des sorties désirées et des entrées discrètes

En entrée comme en sortie, on va généralement représenter les variables discrètes par des groupes d’unités (un groupe de k unités par variable discrète pouvant prendre k valeurs). L’exception pratique est le cas d’une variable binaire, qu’on encode généralement avec une seule unité. Dans le cas des sorties on va associer à chaque groupe une distribution discrète (binomiale pour un seul bit, multinomiale pour une variable discrète générale).

Algorithme d’optimisation

Quand le nombre d’exemples est grand (plusieurs milliers) la descente de gradient stochastique est souvent le meilleur choix (surtout pour la classification), en terme de vitesse et en terme de contrôle de la capacité (il est plus difficile d’avoir de l’overfitting avec la descente de gradient stochastique. En effet, la descente de gradient stochastique ne tombe pas facilement dans les minima très pointus (qui ne généralisent pas bien, car une légére perturbation des données déplaçant la surface d’erreur donnerait une très mauvaise performance), à cause du bruit induit par le pas de gradient et le gradient “bruité”. Ce gradient bruité aide aussi à sortir de certains minima locaux, pour la même raison.

Quand la descente de gradient stochastique est utilisée, il est IMPÉRATIF que les exemples soient bien mélangés: par exemple si on a beaucoup d’exemples consécutifs de la même classe, la convergence sera très lente. Il suffit de permuter aléatoirement les exemples une fois pour toute, pour éliminer toute dépendence entre les exemples successifs. Avec certaines architectures qui captent des dépendences temporelles (signaux, musique, parole, séries chrono, vidéo) on a pas le choix de présenter des séquences dont les éléments sont fortement dépendents, mais on peut mélanger les séquences (l’ensemble d’apprentissage est une suite de séquences).

En principe le pas de gradient devrait être graduellement réduit pendant l’apprentissage pour garantir la convergence asymptotique. Pour certains problèmes (surtout de classification) cela ne semble pas nécessaire, et peut même nuire. Une cédule de descente raisonnable est par exemple \epsilon_t = \frac{\epsilon_0 \tau}{t + \tau} comme discuté ici. Si on pouvait le calculer, le pas de gradient optimal serait \frac{1}{\lambda_{\rm max}}, i.e., l’inverse de la valeur propre la plus grande de la matrice Hessienne, et le pas de gradient maximal (avant divergence) est deux fois plus grand. Le Cun propose une méthode pour estimer efficacemenet \lambda_{\rm max} (voir son tutorial sur le sujet), mais cette technique ne semble pas courramment utilisée.

Quand le nombre d’exemples (et donc de paramètres) est plus petit, et surtout pour la régression, les techniques du second degré (surtout la technique des gradients conjugués) permettent une convergence beaucoup plus rapide. Ces techniques sont batch (modification des paramètres après calcul de l’erreur et gradient sur tous les exemples). Ces techniques sont généralement plus facile à ajuster que la descente de gradient stochastique (moins d’hyper-paramètres ou moins nécessaire de les ajuster par rapport à une valeur par défaut), mais la généralisation est parfois moins bonne à cause de la facilité de tomber dans des minima pointus.

Jusqu’à quelques dizaines de milliers d’exemples, la descente de gradient conjugués reste une des meilleures techniques pour l’optimisation des réseaux de neurones. Au-delà il vaut généralement mieux s’en tenir au gradient stochastique ou à sa version minibatch.

Initialisation des paramètres

On ne peut initialiser tous les poids à zéro sans quoi tous les neurones cachés sont condamnés à toujours faire la même chose (qu’on peut voir par un simple argument de symétrie). On veut aussi éviter la saturation des neurones (sortie près des limites de la non-linéarité, donc gradient presque 0), mais ne pas être trop près initialement d’une fonction linéaire. Quand les paramètres sont tous près de 0, le réseau multicouche calcule une transformation affine (linéaire), donc sa capacité effective par sortie est égale au nombre d’entrées plus 1. En se basant sur ces considérations, le point idéal d’opération du neurone devrait être proche du point d’inflexion de la non-linéarité (entre la partie linéaire près de l’origine et la partie saturation). Par ailleurs, on aimerait que la variance moyenne des valeurs des unités cachées soit préservée quand on propage les “activations” de l’entrée vers la sortie, et de la même manière on aimerait que la variance des gradients le soit aussi quand on les propage de la sortie vers l’entrée. Pour atteindre cet objectif, on peut argumenter que les poids initiaux devraient être initialisés de manière uniforme dans un intervalle [-\sqrt{6/(n_i + n_o)}, \sqrt{6/(n_i+n_o)}], où n_i est le fan-in, i.e., le nombre d’entrées du neurone, le nombre de neurones de la couche précédente, et n_o est le fan-out, i.e., le nombre de neurones de la couche visée. Cela suppose que les entrées sont approximativement uniformes dans l’intervalle (-1,1) (et remarquez comme les sorties des unités cachées tanh sont aussi dans le même intervalle).

Contrôle de la saturation

Un des problèmes fréquents pendant l’apprentissage est la saturation des neurones, souvent dûe à une mauvaise normalisation des entrées ou des sorties désirées ou une mauvaise initialisation des poids, ou bien à l’utilisation de la sigmoide plutôt qu’une fonction de non-linéarité symétrique comme la tanh. On peut contrôler cela on observant la distribution des sorties des neurones (en particulier, la moyenne des valeurs absolues de la somme pondérée est un bon indice). Quand les neurones saturent fréquemment, l’apprentissage est bloqué sur un plateau de la fonction de coût dû à de très petits gradients sur certains paramètres (donc un très mauvais conditionnement du Hessien).

Contrôle de la capacité effective

La théorie du structural risk minimization de Vapnik nous dit qu’il existe une capacité optimale autour de laquelle l’erreur de généralisation augmente (c’est un minimum global et unique). Les techniques de contrôle de la capacité effective visent donc à chercher ce minimum (evidemment de façon approximative).

  • early stopping: il s’agit d’une des techniques les plus populaires et les plus efficaces, mais elle ne marche pas bien quand le nombre d’exemples disponibles est très petit. L’idée est très simple: on utilise un ensemble d’exemples de validation non-utilisés pour l’apprentissage par descente de gradient pour estimer l’erreur de généralisation au fur et à mesure que l’apprentissage itératif progresse (normalement, après chaque époque on mesure l’erreur sur l’ensemble de validation). On garde les paramètres correspondant au minimum de cette courbe d’erreur de généralisation estimée (et on peut s’arrêter quand cette erreur commence à remonter sérieusement ou qu’un minimum a été atteint depuis un certain nombre d’époques). Cela a l’avantage de répondre à une des questions difficile de l’optimisation, qui est: quand arrêter? De plus on remarque qu’on a ainsi choisi pour pas cher (en temps de calcul) un hyper-paramètre important (le nombre d’itérations d’entraînement) qui touche à la fois l’optimisation et la généralisation.

  • contrôle du nombre d’unités cachées: ce nombre influence directement la capacité. Dans ce cas il faut malheureusement faire plusieurs d’expériences d’apprentissage, à moins d’utiliser un algorithme d’apprentissage constructif (qui rajoute des ressources au fur et à mesure), voir l’algorithme de cascade-correlation (Fahlman, 1990). On peut utiliser un ensemble de validation ou la validation croisée pour estimer l’erreur de généralisation. Il faut faire attention au fait que cet estimé est bruité (d’autant plus qu’il y a peu d’exemples de validation). Quand on a plusieurs couches cachées, choisir le même nombre d’unités par couche semble bien fonctionner. Le prix à payer pour un nombre d’unités trop grand est surtout que les calculs sont plus longs, car le nombre accru de paramètre est généralement compensé par le early stopping. Par contre quand le nombre d’unités cachées est trop petit, l’effet sur l’erreur de généralisation et sur l’erreur d’apprentissage peut être beaucoup plus grand. On va généralement choisir la taille des réseau de façon empirique, en gardant ces considérations à l’esprit pour éviter d’avoir à essayer trop de valeurs de la taille.

  • weight decay: c’est une méthode de régularisation (pour contrôler la capacité, empêcher l’overfitting) dont le but est de pénaliser les poids forts. En effet, on peut montrer que la capacité est bornée par la magnitude des poids du réseau de neurones. On rajoute la pénalité

    \lambda \sum_i \theta_i^2

    à la fonction de coût. On l’appelle régularisation L2 car on minimise la norme 2 des paramètres. Certains l’appliquent uniquement aux poids et non pas au biais.

    Comme dans le cas précédent, il faut faire plusieurs expériences d’apprentissage et choisir le facteur de pénalité \lambda (un hyper-paramètre) qui minimise l’erreur de généralisation estimée. On l’estime avec un ensemble de validation ou bien par validation croisée.

    Une forme de régularisation de plus en plus utilisée comme alternative à la régularisation L2 est la régularisation L1, qui a comme avantage que les petits paramètres seront carrément amenés à 0, donnant lieu à un vecteur de paramètres qui est sparse. On va donc minimiser la somme des valeurs absolues des paramètres.