Programmation avec PyTorch: création d'applications d'apprentissage en profondeur

imageBonjour les Habitants! Ian Poynter vous aidera à comprendre comment configurer PyTorch dans le cloud, comment créer des architectures neuronales qui facilitent le travail avec des images, du son et du texte. Le livre couvre les concepts essentiels pour apprendre le portage, le débogage des modèles et l'utilisation de la bibliothèque PyTorch. Vous apprendrez à: - Mettre en œuvre des modèles d'apprentissage en profondeur - Utiliser PyTorch dans des projets à grande échelle - Appliquer le transfert d'apprentissage - Utiliser PyTorch torchaudio et des modèles convolutifs pour classer les données audio - Appliquer les techniques PNL les plus avancées à l'aide d'un modèle formé sur Wikipedia - Déboguer les modèles PyTorch avec TensorBoard et Flamegraph - Déployez des applications PyTorch dans des conteneurs «PyTorch est l'une des bibliothèques d'apprentissage en profondeur à la croissance la plus rapide, rivalisant presque avec le géant de Google TensorFlow.





Classification des images avec PyTorch



Les manuels d'apprentissage en profondeur regorgent de terminologie professionnelle et incompréhensible. J'essaie de le garder au minimum et de toujours donner un exemple qui peut être facilement étendu lorsque vous vous habituez à travailler avec PyTorch. Nous utilisons cet exemple tout au long du livre pour montrer comment déboguer un modèle (chapitre 7) ou le déployer en production (chapitre 8).



A partir de maintenant et jusqu'à la fin du chapitre 4, nous compilerons le classificateur d'images. Les réseaux neuronaux sont couramment utilisés comme classificateurs d'images; les réseaux offrent une image et posent une question simple: "Qu'est-ce que c'est?"



Commençons par créer notre application dans PyTorch.



Problème de classification



Ici, nous allons créer un classificateur simple qui peut distinguer un poisson d'un chat. Nous réitérerons le processus de conception et de développement de notre modèle pour le rendre plus précis.

En figue. 2.1 et 2.2 représentent un poisson et un chat dans toute leur splendeur. Je ne sais pas si le poisson a un nom, mais le nom du chat est Helvetica.



Commençons par discuter de certains des problèmes de classification standard.



image image



Difficultés standard



Comment écrire un programme qui peut distinguer un poisson d'un chat? Vous pourriez peut-être écrire un ensemble de règles décrivant qu'un chat a une queue ou qu'un poisson a des écailles, et appliquer ces règles à l'image afin que le programme puisse classer l'image. Mais cela prendra du temps, des efforts et des compétences. Et si vous tombiez sur un chat Manx? Bien qu'il s'agisse clairement d'un chat, il n'a pas de queue.



Ces règles deviennent de plus en plus complexes lorsque vous essayez de décrire tous les scénarios possibles en les utilisant. De plus, je dois admettre que la programmation visuelle est terrible pour moi, donc l'idée de devoir écrire manuellement le code pour toutes ces règles est terrifiante.



Vous avez besoin d'une fonction qui renvoie un chat ou un poisson lorsque vous entrez une image. Il est difficile de construire une telle fonction en énumérant simplement tous les critères dans leur intégralité. Mais l'apprentissage en profondeur oblige essentiellement l'ordinateur à faire le dur travail de création de toutes ces règles dont nous venons de parler, à condition de créer la structure, de fournir au réseau beaucoup de données et de lui faire savoir s'il a donné la bonne réponse. C'est ce que nous allons faire. De plus, vous apprendrez quelques techniques de base pour utiliser PyTorch.



Mais d'abord les données



Premièrement, nous avons besoin de données. Combien de données? Dépend de divers facteurs. Comme vous le verrez au chapitre 4, l'idée que toute technique d'apprentissage en profondeur nécessite d'énormes quantités de données pour entraîner un réseau de neurones n'est pas nécessairement vraie. Cependant, nous allons maintenant repartir de zéro, ce qui nécessite généralement l'accès à un grand nombre de données. De nombreuses images de poissons et de chats sont nécessaires.



On pourrait passer du temps à télécharger un tas d'images à partir d'une recherche d'images sur Google, mais il existe un moyen plus simple: la collection standard d'images utilisée pour former les réseaux de neurones est ImageNet. Il contient plus de 14 millions d'images et 20 000 catégories d'images. C'est la norme par laquelle tous les classificateurs d'images se comparent. Par conséquent, je prends les images à partir de là, bien que vous puissiez choisir d'autres options si vous le souhaitez.



Outre les données, PyTorch devrait avoir un moyen de définir ce qu'est un chat et ce qu'est un poisson. C'est assez facile pour nous, mais c'est plus difficile pour un ordinateur (c'est pourquoi nous créons un programme!). Nous utilisons un étiquetage attaché aux données et c'est ce qu'on appelle l'apprentissage supervisé. (Si vous n'avez accès à aucune des étiquettes, alors vous l'avez deviné, un apprentissage automatique non supervisé est utilisé.)



Si nous utilisons des données ImageNet, leurs étiquettes ne seront pas utiles car elles contiennent trop d'informations. Étiqueter un chat tigré ou une truite pour un ordinateur n'est pas la même chose qu'un chat ou un poisson.



Il est nécessaire de les renommer. Étant donné qu'ImageNet est une vaste collection d'images, j'ai compilé l'image et les URL de marquage des poissons et des chats (https://oreil.ly/NbtEU).



Vous pouvez exécuter le script download.py dans ce répertoire et il téléchargera les images à partir des URL et les placera dans les emplacements de formation appropriés. Le réétiquetage est simple; le script stocke des images de chats dans le répertoire train / cat et des images de poissons dans le répertoire train / fish. Si vous ne souhaitez pas utiliser de script pour télécharger, créez simplement ces répertoires et placez les images correspondantes aux bons endroits. Nous avons maintenant des données, mais nous devons les convertir dans un format que PyTorch peut comprendre.



PyTorch et chargeurs de données



Le chargement et la transformation de données dans des formats prêts pour la formation est souvent un domaine de la science des données qui prend trop de temps. PyTorch a développé des exigences d'interaction de données établies qui le rendent assez simple, que vous travailliez avec des images, du texte ou de l'audio.



Les deux principales conditions pour travailler avec des données sont les jeux de données et les chargeurs de données. Un ensemble de données est une classe Python qui nous permet d'obtenir les données que nous envoyons au réseau neuronal.



Un chargeur de données est ce qui transfère les données d'un ensemble de données vers le réseau. (Cela peut inclure des informations telles que: Combien de processus de travail téléchargent des données sur le réseau? Combien d'images téléchargeons-nous en même temps?)



Jetons d'abord un coup d'œil à l'ensemble de données. Chaque jeu de données, qu'il contienne des images, de l'audio, du texte, des paysages 3D, des informations boursières ou autre, peut interagir avec PyTorch s'il répond aux exigences de cette classe Python abstraite:



class Dataset(object):
     def __getitem__(self, index):
          raise NotImplementedError

     def __len__(self):
          raise NotImplementedError


C'est assez simple: nous devons utiliser une méthode qui renvoie la taille de notre ensemble de données (len), et une qui peut extraire un élément de l'ensemble de données dans une paire (étiquette, tenseur). Ceci est appelé par le chargeur de données car il transmet les données au réseau neuronal pour l'entraînement. Nous devons donc écrire un corps pour la méthode getitem qui peut prendre une image, la convertir en tenseur et la remettre et la marquer pour que PyTorch puisse travailler avec elle. Tout est clair, mais évidemment ce scénario est assez courant, alors peut-être que PyTorch facilitera la tâche?



Créer un jeu de données d'entraînement



Le paquet torchvision inclut une classe ImageFolder qui fait à peu près tout, en supposant que nos images sont dans une structure où chaque répertoire est une étiquette (par exemple, tous les chats sont dans un répertoire nommé cat). Voici ce dont vous avez besoin pour l'exemple du chat et du poisson:



import torchvision
from torchvision import transforms

train_data_path = "./train/"
transforms = transforms.Compose([
      transforms.Resize(64),
      transforms.ToTensor(),
      transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                std=[0.229, 0.224, 0.225] )
      ])

train_data = torchvision.datasets.ImageFolder
(root=train_data_path,transform=transforms)


Quelque chose d'autre est ajouté ici car torchvision vous permet également de spécifier une liste de transformations à appliquer à l'image avant qu'elle n'entre dans le réseau neuronal. La transformation par défaut consiste à prendre les données d'image et à les transformer en tenseur (la méthode trans forms.ToTensor () montrée dans le code précédent), mais elle fait également quelques autres choses qui pourraient ne pas être aussi évidentes.



Premièrement, les GPU sont conçus pour effectuer des calculs rapides de taille standard. Mais nous avons probablement un assortiment d'images dans de nombreuses résolutions. Pour améliorer les performances de traitement, nous mettons à l'échelle chaque image d'entrée à la même résolution 64x64 à l'aide de la transformation Resize (64). Ensuite, nous convertissons les images en un tenseur et enfin normalisons le tenseur autour d'un ensemble spécifique de points de moyenne et d'écart type.



La normalisation est importante car on s'attend à ce qu'un grand nombre de multiplications soit effectué lorsque l'entrée passe à travers les couches du réseau neuronal; le maintien des valeurs d'entrée entre 0 et 1 empêche de fortes augmentations des valeurs pendant la phase d'apprentissage (connu sous le nom de problème de gradient explosif). Cette incarnation magique n'est que la moyenne et l'écart type de l'ensemble de données ImageNet dans son ensemble. Vous pouvez le calculer spécifiquement pour un sous-ensemble de poissons et de chats, mais ces valeurs sont assez fiables. (Si vous travailliez sur un ensemble de données complètement différent, cette moyenne et cette variance devraient être calculées, bien que beaucoup utilisent simplement des constantes ImageNet et rapportent des résultats acceptables.)



Les transformations composables facilitent également l'exécution d'actions telles que la rotation d'image et le décalage d'image pour l'augmentation des données, sur lesquelles nous reviendrons au chapitre 4.



Dans cet exemple, nous redimensionnons les images en 64 x 64. J'ai fait ce choix aléatoire pour accélérer le calcul sur notre premier réseau. La plupart des architectures existantes, que vous verrez au chapitre 3, utilisent 224x224 ou 299x299 pour leurs images d'entrée. Généralement, plus la taille du fichier d'entrée est grande, plus le réseau peut apprendre de données. Le revers de la médaille est que vous pouvez généralement insérer un plus petit lot d'images dans la mémoire GPU.


Il y a beaucoup d'autres informations sur les ensembles de données, et ce n'est pas tout. Mais pourquoi devrions-nous en savoir plus que ce dont nous avons besoin si nous connaissons déjà l'ensemble de données de formation?



Validation et jeux de données de référence



Notre jeu de données d'entraînement est configuré, mais nous devons maintenant répéter les mêmes étapes avec le jeu de données de validation. Quelle est la différence ici? L'un des pièges de l'apprentissage profond (et en fait de tout l'apprentissage automatique) est le surapprentissage: le modèle est vraiment bon pour reconnaître ce sur quoi il a été formé, mais ne fonctionne pas sur des exemples qu'il n'a pas vus. Le modèle voit une photo d'un chat, et si toutes les autres images de chats ne sont pas très similaires, le modèle décide qu'il ne s'agit pas d'un chat, bien que le contraire soit évident. Pour empêcher le réseau de neurones de se comporter comme ça, nous chargeons l'échantillon de contrôle dans download.py, c'est-à-dire dans une série d'images de chats et de poissons qui ne sont pas dans l'ensemble de données d'entraînement. À la fin de chaque cycle de formation (également appelé époque), nous comparons cet ensemble pour nous assurer que le réseau ne se trompe pas. Ne vous inquiétez pas, le code de cette vérification est incroyablement simple:c'est le même code avec plusieurs noms de variables modifiés:



val_data_path = "./val/"
val_data = torchvision.datasets.ImageFolder(root=val_data_path,
                                                                  transform=transforms)


Nous avons juste utilisé la chaîne de transformations au lieu de la définir à nouveau.



En plus de l'ensemble de données de validation, nous devons également créer un ensemble de données de validation. Il est utilisé pour tester le modèle après avoir terminé toute la formation:



test_data_path = "./test/"
test_data = torchvision.datasets.ImageFolder(root=test_data_path,
                                                                   transform=transforms)


À première vue, les différents types d'ensembles peuvent être complexes et déroutants, j'ai donc rassemblé un tableau pour indiquer quelle partie de la formation utilise chaque ensemble (Tableau 2.1).



image


Nous pouvons maintenant créer des chargeurs de données avec quelques lignes supplémentaires de code Python:



batch_size=64
train_data_loader = data.DataLoader(train_data, batch_size=batch_size)
val_data_loader = data.DataLoader(val_data, batch_size=batch_size)
test_data_loader = data.DataLoader(test_data, batch_size=batch_size)


La commande batch_size est nouvelle et remarquable dans ce code. Elle dit combien d'images passeront par le réseau avant que nous ne nous entraînions et ne le mettions à jour. En théorie, nous pourrions attribuer batch_size à une série d'images dans les ensembles de données de test et de formation afin que le réseau voie chaque image avant l'actualisation. En pratique, cela n'est généralement pas fait car les petits paquets (plus communément appelés mini-paquets dans la littérature) nécessitent moins de mémoire et il n'est pas nécessaire de stocker toutes les informations sur chaque image dans l'ensemble de données, et une taille de paquet plus petite conduit à un apprentissage plus rapide depuis le réseau met à jour beaucoup plus rapidement. Pour les chargeurs de données PyTorch, la valeur batch_size est définie par défaut sur 1. Vous souhaiterez probablement la modifier. Bien que j'aie choisi 64, vous pouvez expérimenter pour comprendrecombien de mini-packages peuvent être utilisés sans manquer de mémoire GPU. Expérimentez avec des paramètres supplémentaires: par exemple, vous pouvez spécifier comment les ensembles de données sont extraits, si l'ensemble de données est mélangé à chaque exécution et combien de flux de travail sont impliqués pour récupérer les données de l'ensemble de données. Tout cela se trouve dansDocumentation PyTorch .



Il s'agit de transmettre des données à PyTorch, alors imaginons maintenant un simple réseau de neurones qui commencera à classer nos images.



Enfin, un réseau de neurones!



Nous allons commencer par le réseau d'apprentissage en profondeur le plus simple - une couche d'entrée qui fonctionnera avec les tenseurs d'entrée (nos images); une couche de sortie de la taille du nombre de nos classes de sortie (2); et une couche cachée entre les deux. Dans le premier exemple, nous utiliserons des couches entièrement liées. En figue. 2.3 montre une couche d'entrée de trois nœuds, une

couche cachée de trois nœuds et une sortie de deux nœuds.



Dans cet exemple, chaque nœud dans une couche affecte un nœud dans la couche suivante, et chaque connexion a un poids qui détermine la force du signal de ce nœud à la couche suivante. (Ce sont les poids qui seront mis à jour lorsque nous formons le réseau, généralement à partir d'une initialisation aléatoire.) Lorsque l'entrée traverse le réseau, nous (ou PyTorch) pouvons simplement multiplier par matrice les poids et les biais de cette couche par l'entrée. Avant de les passer à la fonction suivante, ce résultat entre dans la fonction d'activation, qui est simplement une manière d'introduire la non-linéarité dans notre système.



image


Fonctions d'activation



La fonction d'activation semble délicate, mais la fonction d'activation la plus courante que vous pouvez trouver maintenant est ReLU, ou unité linéaire rectifiée. Encore une fois intelligent! Mais c'est juste une fonction qui implémente max (0, x), donc le résultat est 0 si l'entrée est négative, ou juste l'entrée (x) si x est positif. C'est si simple!



Une autre fonction d'activation que vous êtes le plus susceptible de rencontrer est la fonction logistique multivariée (softmax), qui est un peu plus complexe au sens mathématique. Fondamentalement, il génère un ensemble de valeurs de 0 à 1, qui ajoute jusqu'à 1 (probabilités!), Et pondère les valeurs de manière à augmenter la différence, c'est-à-dire qu'il produit un résultat dans un vecteur qui sera plus grand que tous les autres. Vous le verrez souvent utilisé à la fin d'un réseau de classification pour vous assurer que le réseau fera des prédictions sur la classe qu'il pense que les données d'entrée sont.



Maintenant que nous avons tous ces éléments de base, nous pouvons commencer à construire notre premier réseau neuronal.



Création d'un réseau neuronal



La construction d'un réseau de neurones dans PyTorch est similaire à la programmation en Python. Nous héritons d'une classe appelée torch.nn.Network et remplissons les méthodes __init__ et forward:



class SimpleNet(nn.Module):

def __init__(self):
     super(Net, self).__init__()
     self.fc1 = nn.Linear(12288, 84)
     self.fc2 = nn.Linear(84, 50)
     self.fc3 = nn.Linear(50,2)

def forward(self):
     x = x.view(-1, 12288)
     x = F.relu(self.fc1(x))
     x = F.relu(self.fc2(x))
     x = F.softmax(self.fc3(x))
     return x
simplenet = SimpleNet()


Encore une fois, ce n'est pas difficile. Nous faisons les réglages nécessaires dans init (), dans ce cas, nous appelons le constructeur de superclasse et trois couches entièrement connectées (appelées Linear dans PyTorch, elles sont appelées Dense dans Keras). La méthode forward () décrit comment les données sont transmises sur le réseau, à la fois en apprentissage et en prédiction (inférence). Tout d'abord, nous devons transformer le tenseur 3D (x et y plus les informations de couleur à 3 canaux - rouge, vert, bleu) dans l'image - attention! - dans un tenseur unidimensionnel afin qu'il puisse être passé à la première couche linéaire, et nous le faisons en utilisant view (). Nous appliquons donc les couches et les fonctions d'activation dans l'ordre, en retournant la sortie softmax pour obtenir la prédiction pour cette image.



Les nombres dans les couches cachées sont arbitraires, sauf pour la sortie de la dernière couche, qui est 2, qui correspond à nos deux classes - chat ou poisson. Nécessite que les données des calques soient réduites à mesure qu'elles rétrécissent dans la pile. Si la couche passe, disons, de 50 entrées à 100 sorties, alors le réseau peut apprendre en passant simplement 50 liaisons à cinquante sorties sur cent et considérer son travail comme terminé. En réduisant la taille de la sortie par rapport à l'entrée, nous forçons cette partie du réseau à apprendre la représentativité de l'entrée d'origine avec moins de ressources, ce qui signifie vraisemblablement que le réseau définit certaines caractéristiques distinctives des images: par exemple, il a appris à reconnaître une nageoire ou une queue.



Nous avons une prévision et pouvons la comparer avec l'étiquetage réel de l'image d'origine pour voir si elle était correcte. Mais il faut un moyen pour permettre à PyTorch de quantifier non seulement l'exactitude ou l'inexactitude d'une prédiction, mais aussi son exactitude ou son inexactitude. La fonction de perte fait cela.



A PROPOS DE L'AUTEUR



Ian Poynter (Ian Pointer) - Ingénieur en science des données, spécialisé dans les solutions d'apprentissage automatique (y compris des méthodes d'enseignement approfondies) pour un certain nombre de clients du Fortune 100. Actuellement, Yang travaille chez Lucidworks, qui est engagé dans le développement d'applications avancées et de PNL.



»Plus de détails sur le livre peuvent être trouvés sur le site de la maison d'édition

» Table des matières

» Extrait



Pour Habitants une réduction de 25% sur le coupon - PyTorch



Lors du paiement de la version papier du livre, un e-book est envoyé à l'e-mail.



All Articles