La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants
En aquest article, us explicaré com configurar un entorn d'aprenentatge automàtic en 30 minuts, crear una xarxa neuronal per al reconeixement d'imatges i, a continuació, executar la mateixa xarxa en un processador gràfic (GPU).

Primer, anem a definir què és una xarxa neuronal.

En el nostre cas, es tracta d'un model matemàtic, així com de la seva realització de programari o maquinari, basat en el principi d'organització i funcionament de les xarxes neuronals biològiques: xarxes de cèl·lules nervioses d'un organisme viu. Aquest concepte va sorgir mentre s'estudiaven els processos que ocorren al cervell i s'intentava modelar aquests processos.

Les xarxes neuronals no es programen en el sentit habitual de la paraula, s'entrenen. La capacitat d'aprendre és un dels principals avantatges de les xarxes neuronals respecte als algorismes tradicionals. Tècnicament, l'aprenentatge consisteix a trobar els coeficients de connexions entre neurones. Durant el procés d'entrenament, la xarxa neuronal és capaç d'identificar dependències complexes entre les dades d'entrada i les dades de sortida, així com realitzar una generalització.

Des del punt de vista de l'aprenentatge automàtic, una xarxa neuronal és un cas especial de mètodes de reconeixement de patrons, anàlisi discriminant, mètodes d'agrupació i altres mètodes.

Оборудование

Primer, mirem l'equip. Necessitem un servidor amb el sistema operatiu Linux instal·lat. L'equip necessari per operar sistemes d'aprenentatge automàtic és força potent i, per tant, car. Per a aquells que no tinguin una bona màquina a mà, recomano parar atenció a les ofertes dels proveïdors de núvol. Podeu llogar el servidor necessari ràpidament i pagar només pel temps d'ús.

En projectes on cal crear xarxes neuronals, faig servir els servidors d'un dels proveïdors de núvol russos. L'empresa ofereix servidors en núvol de lloguer específicament per a l'aprenentatge automàtic amb potents processadors gràfics (GPU) Tesla V100 de NVIDIA. En resum: utilitzar un servidor amb una GPU pot ser desenes de vegades més eficient (ràpid) en comparació amb un servidor de cost similar que utilitza una CPU (la coneguda unitat central de processament) per als càlculs. Això s'aconsegueix gràcies a les característiques de l'arquitectura de la GPU, que fa front als càlculs més ràpidament.

Per implementar els exemples que es descriuen a continuació, vam comprar el servidor següent durant diversos dies:

  • Disc SSD de 150 GB
  • RAM 32 GB
  • Processador Tesla V100 de 16 Gb amb 4 nuclis

Hem instal·lat Ubuntu 18.04 a la nostra màquina.

Configuració de l'entorn

Ara instal·lem tot el necessari per treballar al servidor. Com que el nostre article és principalment per a principiants, parlaré d'alguns punts que els seran útils.

Gran part del treball quan es configura un entorn es fa mitjançant la línia d'ordres. La majoria dels usuaris utilitzen Windows com a sistema operatiu. La consola estàndard d'aquest sistema operatiu deixa molt a desitjar. Per tant, utilitzarem una eina convenient Cmder/. Baixeu la versió mini i executeu Cmder.exe. A continuació, heu de connectar-vos al servidor mitjançant SSH:

ssh root@server-ip-or-hostname

En lloc de server-ip-or-hostname, especifiqueu l'adreça IP o el nom DNS del vostre servidor. A continuació, introduïu la contrasenya i si la connexió és correcta, hauríem de rebre un missatge semblant a aquest.

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

El llenguatge principal per desenvolupar models de ML és Python. I la plataforma més popular per al seu ús a Linux és anaconda.

Instal·lem-lo al nostre servidor.

Comencem actualitzant el gestor de paquets local:

sudo apt-get update

Instal·leu curl (utilitat de línia d'ordres):

sudo apt-get install curl

Baixeu la darrera versió d'Anaconda Distribution:

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

Comencem la instal·lació:

bash Anaconda3-2019.10-Linux-x86_64.sh

Durant el procés d'instal·lació, se us demanarà que confirmeu l'acord de llicència. Després de la instal·lació correcta, hauríeu de veure això:

Thank you for installing Anaconda3!

Ara s'han creat molts frameworks per al desenvolupament de models ML; treballem amb els més populars: PyTorch и Corrent de tensió.

L'ús del marc us permet augmentar la velocitat de desenvolupament i utilitzar eines preparades per a tasques estàndard.

En aquest exemple treballarem amb PyTorch. Instal·lem-lo:

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

Ara hem de llançar Jupyter Notebook, una eina de desenvolupament popular per als especialistes en ML. Permet escriure codi i veure immediatament els resultats de la seva execució. Jupyter Notebook s'inclou amb Anaconda i ja està instal·lat al nostre servidor. Cal connectar-s'hi des del nostre sistema d'escriptori.

Per fer-ho, primer llançarem Jupyter al servidor especificant el port 8080:

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

A continuació, obrint una altra pestanya a la nostra consola Cmder (menú superior - Diàleg de la consola nova) ens connectarem mitjançant el port 8080 al servidor mitjançant SSH:

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

Quan introduïm la primera ordre, se'ns oferiran enllaços per obrir Jupyter al nostre navegador:

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

Utilitzem l'enllaç per a localhost:8080. Copieu el camí complet i enganxeu-lo a la barra d'adreces del navegador local del vostre ordinador. S'obrirà Jupyter Notebook.

Creem un quadern nou: Nou - Llibreta - Python 3.

Comprovem el correcte funcionament de tots els components que hem instal·lat. Introduïm l'exemple de codi PyTorch a Jupyter i executem l'execució (botó Executar):

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

El resultat hauria de ser una cosa així:

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

Si teniu un resultat semblant, llavors ho hem configurat tot correctament i podem començar a desenvolupar una xarxa neuronal!

Creació d'una xarxa neuronal

Crearem una xarxa neuronal per al reconeixement d'imatges. Prenem això com a base lideratge.

Utilitzarem el conjunt de dades CIFAR10 disponible públicament per entrenar la xarxa. Té classes: “avió”, “cotxe”, “ocell”, “gat”, “cérvol”, “gos”, “granota”, “cavall”, “vaixell”, “camió”. Les imatges de CIFAR10 són 3x32x32, és a dir, imatges en color de 3 canals de 32x32 píxels.

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants
Per treballar, utilitzarem el paquet creat per PyTorch per treballar amb imatges - torchvision.

Farem els següents passos per ordre:

  • Càrrega i normalització de conjunts de dades d'entrenament i proves
  • Definició de xarxa neuronal
  • Formació en xarxa sobre dades de formació
  • Proves de xarxa amb dades de prova
  • Repetim l'entrenament i les proves amb GPU

Executarem tot el codi següent a Jupyter Notebook.

Càrrega i normalització CIFAR10

Copieu i executeu el codi següent a 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 resposta hauria de ser:

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

Mostrem diverses imatges d'entrenament per provar:


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)))

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

Definició de xarxa neuronal

Considerem primer com funciona una xarxa neuronal per al reconeixement d'imatges. Aquesta és una xarxa simple punt a punt. Pren dades d'entrada, les passa per diverses capes una per una i, finalment, produeix dades de sortida.

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

Creem una xarxa similar al nostre entorn:


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()

També definim una funció de pèrdua i un optimitzador


import torch.optim as optim

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

Formació en xarxa sobre dades de formació

Comencem a entrenar la nostra xarxa neuronal. Tingueu en compte que després d'executar aquest codi, haureu d'esperar un temps fins que s'acabi el treball. Em va costar 5 minuts. Es necessita temps per entrenar la xarxa.

 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')

Obtenim el següent resultat:

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

Guardem el nostre model entrenat:

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

Proves de xarxa amb dades de prova

Hem entrenat la xarxa mitjançant un conjunt de dades d'entrenament. Però hem de comprovar si la xarxa ha après alguna cosa.

Ho provarem predint l'etiqueta de classe que emet la xarxa neuronal i provant-la per veure si és certa. Si la predicció és correcta, afegim la mostra a la llista de prediccions correctes.
Mostrem una imatge del conjunt de proves:

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)))

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

Ara demanem a la xarxa neuronal que ens digui què hi ha a aquestes imatges:


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)))

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

Els resultats semblen força bons: la xarxa va identificar correctament tres de les quatre imatges.

Vegem com funciona la xarxa a tot el conjunt de dades.


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))

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

Sembla que la xarxa sap alguna cosa i està funcionant. Si determinés les classes a l'atzar, la precisió seria del 10%.

Ara vegem quines classes identifica millor la xarxa:

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]))

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

Sembla que la xarxa és la millor per identificar cotxes i vaixells: 71% de precisió.

Així que la xarxa funciona. Ara intentem transferir el seu treball al processador gràfic (GPU) i veure què canvia.

Entrenament d'una xarxa neuronal en GPU

En primer lloc, explicaré breument què és CUDA. CUDA (Compute Unified Device Architecture) és una plataforma de computació paral·lela desenvolupada per NVIDIA per a la computació general en unitats de processament gràfic (GPU). Amb CUDA, els desenvolupadors poden accelerar dràsticament les aplicacions informàtiques aprofitant el poder de les GPU. Aquesta plataforma ja està instal·lada al nostre servidor que vam comprar.

Primer definim la nostra GPU com el primer dispositiu 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 )

La vostra primera xarxa neuronal en una unitat de processament de gràfics (GPU). Guia per a principiants

Enviament de la xarxa a la GPU:

net.to(device)

També haurem d'enviar entrades i objectius a cada pas a la GPU:

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

Tornem a entrenar la xarxa a la 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')

Aquesta vegada, la formació en xarxa va durar uns 3 minuts. Recordem que la mateixa etapa en un processador convencional durava 5 minuts. La diferència no és significativa, això passa perquè la nostra xarxa no és tan gran. Quan s'utilitzen matrius grans per a l'entrenament, augmentarà la diferència entre la velocitat de la GPU i un processador tradicional.

Això sembla ser tot. Què hem aconseguit fer:

  • Hem mirat què és una GPU i hem seleccionat el servidor on està instal·lada;
  • Hem configurat un entorn de programari per crear una xarxa neuronal;
  • Vam crear una xarxa neuronal per al reconeixement d'imatges i la vam entrenar;
  • Vam repetir l'entrenament de la xarxa mitjançant la GPU i vam rebre un augment de velocitat.

Estaré encantat de respondre les preguntes als comentaris.

Font: www.habr.com

Afegeix comentari