Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant
Dans cet article, je vais vous expliquer comment configurer un environnement d'apprentissage automatique en 30 minutes, créer un réseau neuronal pour la reconnaissance d'images, puis exécuter le même réseau sur un processeur graphique (GPU).

Tout d’abord, définissons ce qu’est un réseau de neurones.

Dans notre cas, il s'agit d'un modèle mathématique, ainsi que de son incarnation logicielle ou matérielle, construit sur le principe d'organisation et de fonctionnement des réseaux de neurones biologiques - réseaux de cellules nerveuses d'un organisme vivant. Ce concept est né en étudiant les processus se produisant dans le cerveau et en essayant de modéliser ces processus.

Les réseaux de neurones ne sont pas programmés au sens habituel du terme, ils sont entraînés. La capacité d’apprendre est l’un des principaux avantages des réseaux de neurones par rapport aux algorithmes traditionnels. Techniquement, l’apprentissage consiste à trouver les coefficients de connexions entre neurones. Au cours du processus de formation, le réseau neuronal est capable d'identifier des dépendances complexes entre les données d'entrée et les données de sortie, ainsi que d'effectuer une généralisation.

Du point de vue de l'apprentissage automatique, un réseau de neurones est un cas particulier de méthodes de reconnaissance de formes, d'analyse discriminante, de méthodes de clustering et d'autres méthodes.

équipement

Voyons d’abord l’équipement. Nous avons besoin d'un serveur sur lequel le système d'exploitation Linux est installé. L’équipement requis pour faire fonctionner les systèmes d’apprentissage automatique est assez puissant et, par conséquent, coûteux. Pour ceux qui n’ont pas une bonne machine sous la main, je recommande de prêter attention aux offres des fournisseurs de cloud. Vous pouvez louer rapidement le serveur requis et payer uniquement pour le temps d'utilisation.

Dans les projets où il est nécessaire de créer des réseaux de neurones, j'utilise les serveurs de l'un des fournisseurs de cloud russes. La société propose à la location des serveurs cloud spécifiquement pour l'apprentissage automatique avec de puissants processeurs graphiques (GPU) Tesla V100 de NVIDIA. En bref : utiliser un serveur avec un GPU peut être des dizaines de fois plus efficace (rapide) par rapport à un serveur de coût similaire qui utilise un CPU (la célèbre unité centrale) pour les calculs. Ceci est obtenu grâce aux caractéristiques de l'architecture GPU, qui effectue les calculs plus rapidement.

Pour mettre en œuvre les exemples décrits ci-dessous, nous avons acheté le serveur suivant pendant plusieurs jours :

  • Disque SSD 150 Go
  • RAM 32 Go
  • Processeur Tesla V100 16 Go avec 4 cœurs

Nous avons installé Ubuntu 18.04 sur notre machine.

Mise en place de l'environnement

Installons maintenant tout le nécessaire pour travailler sur le serveur. Puisque notre article s'adresse avant tout aux débutants, je vais aborder quelques points qui leur seront utiles.

Une grande partie du travail lors de la configuration d’un environnement est effectuée via la ligne de commande. La plupart des utilisateurs utilisent Windows comme système d'exploitation. La console standard de ce système d'exploitation laisse beaucoup à désirer. Par conséquent, nous utiliserons un outil pratique Commande/. Téléchargez la version mini et exécutez Cmder.exe. Ensuite, vous devez vous connecter au serveur via SSH :

ssh root@server-ip-or-hostname

Au lieu de l'adresse IP du serveur ou du nom d'hôte, spécifiez l'adresse IP ou le nom DNS de votre serveur. Ensuite, entrez le mot de passe et si la connexion réussit, nous devrions recevoir un message similaire à celui-ci.

Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-74-generic x86_64)

Le langage principal pour développer des modèles ML est Python. Et la plate-forme la plus populaire pour son utilisation sous Linux est Anaconda.

Installons-le sur notre serveur.

Nous commençons par mettre à jour le gestionnaire de packages local :

sudo apt-get update

Installez curl (utilitaire de ligne de commande) :

sudo apt-get install curl

Téléchargez la dernière version d'Anaconda Distribution :

cd /tmp
curl –O https://repo.anaconda.com/archive/Anaconda3-2019.10-Linux-x86_64.sh

Commençons l'installation :

bash Anaconda3-2019.10-Linux-x86_64.sh

Pendant le processus d'installation, il vous sera demandé de confirmer le contrat de licence. Une fois l'installation réussie, vous devriez voir ceci :

Thank you for installing Anaconda3!

De nombreux frameworks ont désormais été créés pour le développement de modèles ML ; nous travaillons avec les plus populaires : PyTorch и Tensorflow.

L'utilisation du framework vous permet d'augmenter la vitesse de développement et d'utiliser des outils prêts à l'emploi pour des tâches standard.

Dans cet exemple, nous travaillerons avec PyTorch. Installons-le :

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

Nous devons maintenant lancer Jupyter Notebook, un outil de développement populaire pour les spécialistes du ML. Il vous permet d'écrire du code et de voir immédiatement les résultats de son exécution. Jupyter Notebook est inclus avec Anaconda et est déjà installé sur notre serveur. Vous devez vous y connecter depuis notre système de bureau.

Pour cela, nous allons d'abord lancer Jupyter sur le serveur en précisant le port 8080 :

jupyter notebook --no-browser --port=8080 --allow-root

Ensuite, en ouvrant un autre onglet dans notre console Cmder (menu supérieur - Nouvelle boîte de dialogue console), nous nous connecterons via le port 8080 au serveur via SSH :

ssh -L 8080:localhost:8080 root@server-ip-or-hostname

Lorsque nous entrons la première commande, des liens nous seront proposés pour ouvrir Jupyter dans notre navigateur :

To access the notebook, open this file in a browser:
        file:///root/.local/share/jupyter/runtime/nbserver-18788-open.html
    Or copy and paste one of these URLs:
        http://localhost:8080/?token=cca0bd0b30857821194b9018a5394a4ed2322236f116d311
     or http://127.0.0.1:8080/?token=cca0bd0b30857821194b9018a5394a4ed2322236f116d311

Utilisons le lien pour localhost:8080. Copiez le chemin complet et collez-le dans la barre d'adresse du navigateur local de votre PC. Le bloc-notes Jupyter s'ouvrira.

Créons un nouveau notebook : Nouveau - Notebook - Python 3.

Vérifions le bon fonctionnement de tous les composants que nous avons installés. Entrons l'exemple de code PyTorch dans Jupyter et exécutons l'exécution (bouton Exécuter) :

from __future__ import print_function
import torch
x = torch.rand(5, 3)
print(x)

Le résultat devrait ressembler à ceci :

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

Si vous obtenez un résultat similaire, alors nous avons tout configuré correctement et nous pouvons commencer à développer un réseau de neurones !

Créer un réseau de neurones

Nous allons créer un réseau de neurones pour la reconnaissance d'images. Prenons ceci comme base руководство.

Nous utiliserons l'ensemble de données CIFAR10 accessible au public pour former le réseau. Il comporte des classes : « avion », « voiture », « oiseau », « chat », « cerf », « chien », « grenouille », « cheval », « bateau », « camion ». Les images dans CIFAR10 sont au format 3x32x32, c'est-à-dire des images couleur à 3 canaux de 32x32 pixels.

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant
Pour le travail, nous utiliserons le package créé par PyTorch pour travailler avec des images - torchvision.

Nous allons suivre les étapes suivantes dans l'ordre :

  • Chargement et normalisation des ensembles de données de formation et de test
  • Définition du réseau neuronal
  • Formation en réseau sur les données de formation
  • Tests de réseau sur des données de test
  • Répétons la formation et les tests à l'aide du GPU

Nous exécuterons tout le code ci-dessous dans Jupyter Notebook.

Chargement et normalisation de CIFAR10

Copiez et exécutez le code suivant dans Jupyter :


import torch
import torchvision
import torchvision.transforms as transforms

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

La réponse devrait être :

Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
Extracting ./data/cifar-10-python.tar.gz to ./data
Files already downloaded and verified

Affichons plusieurs images de formation à des fins de test :


import matplotlib.pyplot as plt
import numpy as np

# functions to show an image

def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()

# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

Définition du réseau neuronal

Voyons d'abord comment fonctionne un réseau de neurones pour la reconnaissance d'images. Il s'agit d'un simple réseau point à point. Il prend les données d'entrée, les fait passer à travers plusieurs couches une par une, puis produit finalement des données de sortie.

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

Créons un réseau similaire dans notre environnement :


import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

Nous définissons également une fonction de perte et un optimiseur


import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

Formation en réseau sur les données de formation

Commençons à entraîner notre réseau neuronal. Veuillez noter qu'après avoir exécuté ce code, vous devrez attendre un certain temps jusqu'à ce que le travail soit terminé. Cela m'a pris 5 minutes. Il faut du temps pour former le réseau.

 for epoch in range(2):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')

On obtient le résultat suivant :

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

Nous sauvegardons notre modèle entraîné :

PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)

Tests de réseau sur des données de test

Nous avons formé le réseau à l'aide d'un ensemble de données de formation. Mais nous devons vérifier si le réseau a appris quelque chose.

Nous allons tester cela en prédisant l'étiquette de classe générée par le réseau neuronal et en la testant pour voir si elle est vraie. Si la prédiction est correcte, nous ajoutons l’échantillon à la liste des prédictions correctes.
Montrons une image de l'ensemble de test :

dataiter = iter(testloader)
images, labels = dataiter.next()

# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

Demandons maintenant au réseau neuronal de nous dire ce qu’il y a dans ces images :


net = Net()
net.load_state_dict(torch.load(PATH))

outputs = net(images)

_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

Les résultats semblent plutôt bons : le réseau a correctement identifié trois images sur quatre.

Voyons comment le réseau fonctionne sur l'ensemble de l'ensemble de données.


correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

On dirait que le réseau sait quelque chose et fonctionne. S'il déterminait les classes au hasard, la précision serait de 10 %.

Voyons maintenant quelles classes le réseau identifie le mieux :

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1


for i in range(10):
    print('Accuracy of %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i]))

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

Il semble que le réseau soit le meilleur pour identifier les voitures et les navires : 71 % de précision.

Le réseau fonctionne donc. Essayons maintenant de transférer son travail vers le processeur graphique (GPU) et voyons quels changements.

Entraîner un réseau de neurones sur GPU

Tout d’abord, je vais expliquer brièvement ce qu’est CUDA. CUDA (Compute Unified Device Architecture) est une plateforme de calcul parallèle développée par NVIDIA pour le calcul général sur les unités de traitement graphique (GPU). Avec CUDA, les développeurs peuvent accélérer considérablement les applications informatiques en tirant parti de la puissance des GPU. Cette plateforme est déjà installée sur notre serveur que nous avons acheté.

Définissons d'abord notre GPU comme premier périphérique cuda visible.

device = torch . device ( "cuda:0" if torch . cuda . is_available () else "cpu" )
# Assuming that we are on a CUDA machine, this should print a CUDA device:
print ( device )

Votre premier réseau de neurones sur une unité de traitement graphique (GPU). Guide du débutant

Envoi du réseau vers le GPU :

net.to(device)

Nous devrons également envoyer des entrées et des cibles à chaque étape au GPU :

inputs, labels = data[0].to(device), data[1].to(device)

Re-entraînons le réseau sur le GPU :

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
for epoch in range(2):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
    inputs, labels = data[0].to(device), data[1].to(device)

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')

Cette fois, la formation en réseau a duré environ 3 minutes. Rappelons que la même étape sur un processeur classique durait 5 minutes. La différence n’est pas significative, cela se produit parce que notre réseau n’est pas si grand. Lors de l'utilisation de grandes baies pour l'entraînement, la différence entre la vitesse du GPU et celle d'un processeur traditionnel augmentera.

Cela semble être tout. Ce que nous avons réussi à faire :

  • Nous avons regardé ce qu'est un GPU et sélectionné le serveur sur lequel il est installé ;
  • Nous avons mis en place un environnement logiciel pour créer un réseau de neurones ;
  • Nous avons créé un réseau neuronal pour la reconnaissance d'images et l'avons formé ;
  • Nous avons répété la formation réseau à l'aide du GPU et avons obtenu une augmentation de vitesse.

Je me ferai un plaisir de répondre aux questions dans les commentaires.

Source: habr.com

Ajouter un commentaire