Le test unitaire est ton ami
Il y a un dicton parmi les écrivains qui dit: "Toute écriture est en train de ré-écrire" - c'est-à-dire que la majeure partie de l'écriture est en révision. Pour les programmeurs (ou au moins les scientifiques de données), l'expression pourrait être reformulée comme suit: "Tout le codage est un débogage".
Chaque fois que vous écrivez du code, vous devez vérifier qu'il fonctionne comme prévu. La meilleure méthode que j'ai jamais trouvée pour vérifier l'exactitude consiste à diviser votre code en petits segments et à vérifier que chaque segment fonctionne. Cela peut être fait en comparant la sortie du segment à ce que vous savez être la bonne réponse. Ceci s'appelle le test unitaire . Écrire de bons tests unitaires est essentiel pour devenir un bon statisticien / expert en données / expert en apprentissage automatique / praticien des réseaux neuronaux. Il n'y a tout simplement pas de substitut.
Vous devez vérifier que votre code est exempt de bugs avant de pouvoir ajuster les performances du réseau! Sinon, vous pourriez aussi bien réorganiser les chaises longues sur le RMS Titanic .
Deux caractéristiques des réseaux de neurones rendent la vérification encore plus importante que pour d’autres types d’apprentissage automatique ou de modèles statistiques.
Les réseaux de neurones ne sont pas des algorithmes "standard" au sens de la régression aléatoire par forêt ou logistique. Même pour les réseaux simples à feed-forward, il incombe en grande partie à l'utilisateur de prendre de nombreuses décisions concernant la configuration, la connexion, l'initialisation et l'optimisation du réseau. Cela signifie écrire du code et écrire du code signifie déboguer.
Même lorsqu'un code de réseau neuronal s'exécute sans lever d'exception, le réseau peut toujours présenter des bugs! Ces bogues pourraient même être du type insidieux pour lequel le réseau s’entraînera, mais rester bloqué à une solution sous-optimale, ou le réseau résultant ne possède pas l’architecture souhaitée. ( Ceci est un exemple de la différence entre une erreur syntaxique et sémantique .)
Ce billet de niveau moyen , " Comment tester un code d’apprentissage machine ", de Chase Roberts, décrit plus en détail les tests unitaires pour les modèles d’apprentissage automatique. J'ai emprunté cet exemple de code buggy de l'article:
def make_convnet(input_image):
net = slim.conv2d(input_image, 32, [11, 11], scope="conv1_11x11")
net = slim.conv2d(input_image, 64, [5, 5], scope="conv2_5x5")
net = slim.max_pool2d(net, [4, 4], stride=4, scope='pool1')
net = slim.conv2d(input_image, 64, [5, 5], scope="conv3_5x5")
net = slim.conv2d(input_image, 128, [3, 3], scope="conv4_3x3")
net = slim.max_pool2d(net, [2, 2], scope='pool2')
net = slim.conv2d(input_image, 128, [3, 3], scope="conv5_3x3")
net = slim.max_pool2d(net, [2, 2], scope='pool3')
net = slim.conv2d(input_image, 32, [1, 1], scope="conv6_1x1")
return net
Voyez-vous l'erreur? Beaucoup des différentes opérations ne sont pas réellement utilisées car les résultats précédents sont remplacés par de nouvelles variables. L'utilisation de ce bloc de code dans un réseau entraîne toujours la formation et les poids vont être mis à jour et la perte peut même diminuer - mais le code ne fait certainement pas ce qui était prévu. (L’auteur est également incohérent quant à l’utilisation de guillemets simples ou doubles, mais c’est purement stylistique.)
Les erreurs de programmation les plus courantes relatives aux réseaux de neurones sont
- Les variables sont créées mais jamais utilisées (généralement à cause d'erreurs copier / coller);
- Les expressions pour les mises à jour en dégradé sont incorrectes.
- Les mises à jour de poids ne sont pas appliquées;
- Les fonctions de perte ne sont pas mesurées sur la bonne échelle (par exemple, la perte d'entropie croisée peut être exprimée en termes de probabilité ou de logits)
- La perte n'est pas appropriée pour la tâche (par exemple, utilisation de la perte catégorique par entropie croisée pour une tâche de régression).
Ramper avant de marcher; Marchez avant de courir
Les réseaux neuronaux larges et profonds, ainsi que les réseaux neuronaux avec câblage exotique, constituent actuellement le point chaud de l'apprentissage automatique. Mais ces réseaux ne naissent pas complètement formés. leurs concepteurs ont construit pour eux à partir de petites unités. Commencez par créer un petit réseau avec une seule couche masquée et vérifiez qu’il fonctionne correctement. Ajoutez ensuite progressivement la complexité du modèle et vérifiez que chacun de ces éléments fonctionne également.
Trop peu de neurones dans une couche peuvent restreindre la représentation apprise par le réseau, entraînant un sous-ajustement. Trop de neurones peuvent provoquer une surcharge car le réseau «mémorisera» les données d'apprentissage.
Même si vous pouvez prouver mathématiquement que seul un petit nombre de neurones est nécessaire pour modéliser un problème, il est fréquent que le fait de disposer de "quelques" neurones supplémentaires permette à l'optimiseur de trouver plus facilement une "bonne" configuration. (Mais je ne pense pas que quiconque comprenne parfaitement pourquoi c'est le cas.) Je donne un exemple de cela dans le contexte du problème XOR ici: Mes itérations ne sont-elles pas nécessaires pour former NN pour XOR avec une MSE <0,001 trop élevée? .
Le choix du nombre de couches masquées permet au réseau d’apprendre une abstraction à partir des données brutes. L'apprentissage en profondeur fait fureur ces jours-ci, et les réseaux avec un grand nombre de couches ont donné des résultats impressionnants. Mais ajouter trop de couches cachées peut rendre le processus trop compliqué ou rendre très difficile l'optimisation du réseau.
Choisir un câblage de réseau intelligent peut faire beaucoup de travail pour vous. Votre source de données est-elle adaptée aux architectures de réseau spécialisées? Les réseaux de neurones convolutifs peuvent obtenir des résultats impressionnants avec des sources de données "structurées", des données image ou audio. Les réseaux de neurones récurrents peuvent bien fonctionner avec des types de données séquentiels, tels que des données en langage naturel ou des séries chronologiques. Les connexions résiduelles peuvent améliorer les réseaux à feed-forward profonds.
La formation de réseau de neurones est comme le crochetage
Pour obtenir des résultats à la pointe de la technologie, voire simplement de bons résultats, vous devez avoir configuré toutes les pièces configurées pour fonctionner ensemble . Configurer une configuration de réseau neuronal qui apprend réellement ressemble beaucoup à la sélection d’un verrou: toutes les pièces doivent être alignées correctement. Tout comme il ne suffit pas d'avoir un seul gobelet au bon endroit, il ne suffit pas non plus de ne configurer que l'architecture ou l'optimiseur correctement.
Régler les choix de configuration n’est pas aussi simple que de dire qu’un type de choix de configuration (taux d’apprentissage, par exemple) est plus ou moins important qu’un autre (nombre d’unités, par exemple), car tous ces choix interagissent avec tous les autres choix. le choix peut bien faire en combinaison avec un autre choix fait ailleurs .
Ceci est une liste non exhaustive des options de configuration qui ne sont pas non plus des options de régularisation ou des options d'optimisation numérique.
Tous ces sujets sont des domaines de recherche actifs.
L'optimisation non convexe est difficile
La fonction objective d'un réseau de neurones n'est convexe que lorsqu'il n'y a pas d'unités cachées, que toutes les activations sont linéaires et que la matrice de conception est à rang plein - car cette configuration est identique- ment un problème de régression ordinaire.
Dans tous les autres cas, le problème d'optimisation est non convexe et l'optimisation non convexe est difficile. Les défis de la formation des réseaux de neurones sont bien connus (voir: Pourquoi est-il difficile de former des réseaux de neurones profonds? ). De plus, les réseaux de neurones ont un très grand nombre de paramètres, ce qui nous limite aux méthodes de premier ordre (voir: Pourquoi la méthode de Newton n'est-elle pas largement utilisée dans l'apprentissage automatique? ). C'est un domaine de recherche très actif.
Réglage du taux d' apprentissage trop important entraînera l'optimisation de diverger, parce que vous sauter d'un côté du « canyon » à l'autre. Définir ce paramètre trop petit vous empêchera de faire de réels progrès et permettra probablement au bruit inhérent à SGD de submerger vos estimations de gradient.
L'écrêtage du dégradé redimensionne la norme du dégradé s'il dépasse un certain seuil. J'avais l'habitude de penser qu'il s'agissait d'un paramètre "set-and-oublier", généralement à 1,0, mais j'ai constaté que je pouvais améliorer considérablement le modèle de langage LSTM en le réglant sur 0,25. Je ne sais pas pourquoi.
La planification du taux d'apprentissage peut diminuer le taux d'apprentissage au cours de la formation. D'après mon expérience, essayer d'utiliser l'ordonnancement ressemble beaucoup à regex : il remplace un problème ("Comment apprendre à continuer après une certaine époque?") Par deux problèmes ("Comment apprendre à continuer après une certaine époque?" ? "et" Comment choisir un bon programme? "). D'autres personnes insistent sur le fait que la planification est essentielle. Je vous laisse décider.
Le choix d’une taille de mini-lot correcte peut influencer indirectement le processus d’apprentissage, puisqu’un mini-lot plus grand aura tendance à présenter une variance moins grande ( loi des grands nombres ) qu’un mini-lot plus petit. Vous souhaitez que le mini-lot soit suffisamment volumineux pour indiquer la direction du gradient, mais suffisamment petit pour que SGD puisse régulariser votre réseau.
Il existe un certain nombre de variantes sur la descente de gradient stochastique qui utilisent l'impulsion, les vitesses d'apprentissage adaptatives, les mises à jour de Nesterov, etc. pour améliorer le SGD vanille. Concevoir un meilleur optimiseur est un domaine de recherche actif. Quelques exemples:
À sa sortie, l'optimiseur Adam a suscité beaucoup d'intérêt. Toutefois, des recherches récentes ont montré que SGD avec élan pouvait dépasser les méthodes de gradient adaptatives pour les réseaux de neurones. " La valeur marginale des méthodes de gradients adaptatifs dans l'apprentissage automatique " par Ashia C. Wilson, Rebecca Roelofs, Mitchell Stern, Nathan Srebro et Benjamin Recht
Par ailleurs, cet article très récent propose un nouvel optimiseur de taux d’apprentissage adaptatif censé combler l’écart entre les méthodes de taux adaptatif et SGD avec élan. " Combler les lacunes en matière de généralisation des méthodes de gradients adaptatifs dans la formation de réseaux neuronaux profonds " par Jinghui Chen, Quanquan Gu
Les méthodes de gradient adaptatif, qui adoptent des informations de gradient historiques pour ajuster automatiquement le taux d'apprentissage, se sont avérées généraliser moins que la descente de gradient stochastique (SGD) avec dynamique dans la formation de réseaux neuronaux profonds. Cela laisse la question de savoir comment combler l’écart de généralisation des méthodes de gradient adaptatif. Dans ce travail, nous montrons que les méthodes de gradient adaptatives telles que Adam, Amsgrad, sont parfois "trop adaptées". Nous concevons un nouvel algorithme, appelé méthode d'estimation partiellement adaptative du moment (Padam), qui unifie Adam / Amsgrad avec SGD pour obtenir le meilleur des deux mondes. Des expériences sur des points de référence standard montrent que Padam peut maintenir une vitesse de convergence rapide identique à celle d’Adam / Amsgrad tout en généralisant, ainsi que le SGD dans l’entraînement de réseaux neuronaux profonds.
Normalisation
L'ampleur des données peut faire une grande différence en matière de formation.
[ - 0.5 , 0.5 ]
La normalisation des couches peut améliorer la formation du réseau en gardant une moyenne et un écart standard en cours d’exécution pour les activations des neurones. On ne comprend pas bien pourquoi cela facilite la formation et reste un domaine de recherche actif.
Régularisation
Le choix et le réglage de la régularisation du réseau sont des éléments clés de la construction d’un modèle qui généralise bien (c’est-à-dire un modèle qui n’est pas sur-ajusté aux données de formation). Cependant, au moment où votre réseau s'efforce de réduire la perte de données d'apprentissage - lorsque le réseau n'apprend pas - la régularisation peut masquer le problème.
Lorsque mon réseau n'apprend pas, je désactive toute régularisation et vérifie que le réseau non régularisé fonctionne correctement. Ensuite, je rajoute chaque pièce de régularisation et vérifie que chacune d’elles fonctionne en cours de route.
Cette tactique peut indiquer où une régularisation pourrait être mal définie. Certains exemples sont
L2L1
Deux parties de la régularisation sont en conflit. Par exemple, il est largement observé que la normalisation des couches et l’abandon sont difficiles à utiliser ensemble. Étant donné que l'un ou l'autre est en soi très utile, comprendre comment utiliser les deux constitue un domaine de recherche actif.
Tenir un journal de bord des expériences
Lorsque je configure un réseau de neurones, je ne code pas dans les paramètres. À la place, je le fais dans un fichier de configuration (par exemple, JSON) qui est lu et utilisé pour renseigner les détails de la configuration du réseau au moment de l'exécution. Je garde tous ces fichiers de configuration. Si je modifie un paramètre, je crée un nouveau fichier de configuration. Enfin, j’ajoute comme commentaire toutes les pertes par époque pour la formation et la validation.
k
Par exemple, je voulais en savoir plus sur les modèles de langage LSTM. J'ai donc décidé de créer un bot Twitter qui écrit de nouveaux tweets en réponse à d'autres utilisateurs de Twitter. J'y ai travaillé pendant mon temps libre, entre les études supérieures et mon travail. Cela a pris environ un an et j'ai parcouru environ 150 modèles différents avant de créer un modèle qui répond à mes attentes: générer un nouveau texte en langue anglaise qui (en quelque sorte) a du sens. (Un point de blocage important, et une partie de la raison pour laquelle il a fallu tant de tentatives, est qu'il n'était pas suffisant d'obtenir simplement une faible perte hors échantillon, car des modèles précoces à faible perte avaient réussi à mémoriser les données de formation, il ne s'agissait donc que de reproduire textuellement des blocs de texte pertinents en réponse à des invites - il a fallu quelques ajustements pour rendre le modèle plus spontané et conserver une faible perte.)