La tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

La tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti
In questo articolo ti spiegherò come configurare un ambiente di apprendimento automatico in 30 minuti, creare una rete neurale per il riconoscimento delle immagini e quindi eseguire la stessa rete su un processore grafico (GPU).

Innanzitutto, definiamo cos'è una rete neurale.

Nel nostro caso, questo è un modello matematico, così come la sua incarnazione software o hardware, costruito sul principio di organizzazione e funzionamento delle reti neurali biologiche - reti di cellule nervose di un organismo vivente. Questo concetto è nato studiando i processi che si verificano nel cervello e cercando di modellare questi processi.

Le reti neurali non sono programmate nel senso comune del termine, ma addestrate. La capacità di apprendere è uno dei principali vantaggi delle reti neurali rispetto agli algoritmi tradizionali. Tecnicamente l'apprendimento consiste nel trovare i coefficienti delle connessioni tra i neuroni. Durante il processo di addestramento, la rete neurale è in grado di identificare dipendenze complesse tra dati di input e dati di output, nonché di eseguire generalizzazioni.

Dal punto di vista dell'apprendimento automatico, una rete neurale è un caso speciale di metodi di riconoscimento di modelli, analisi discriminante, metodi di clustering e altri metodi.

Attrezzatura

Per prima cosa, diamo un'occhiata all'attrezzatura. Abbiamo bisogno di un server su cui sia installato il sistema operativo Linux. L’attrezzatura necessaria per far funzionare i sistemi di apprendimento automatico è piuttosto potente e, di conseguenza, costosa. Per chi non ha una buona macchina a portata di mano consiglio di prestare attenzione alle offerte dei provider cloud. Puoi noleggiare velocemente il server richiesto e pagare solo per il tempo di utilizzo.

Nei progetti in cui è necessario creare reti neurali, utilizzo i server di uno dei fornitori di servizi cloud russi. L'azienda offre server cloud a noleggio appositamente per l'apprendimento automatico con potenti processori grafici (GPU) Tesla V100 di NVIDIA. In breve: utilizzare un server dotato di GPU può essere decine di volte più efficiente (veloce) rispetto a un server di costo simile che utilizza una CPU (la nota unità di elaborazione centrale) per i calcoli. Ciò è ottenuto grazie alle caratteristiche dell'architettura GPU, che gestisce i calcoli più velocemente.

Per implementare gli esempi descritti di seguito, abbiamo acquistato per diversi giorni il seguente server:

  • Disco SSD 150 GB
  • RAM 32GB
  • Processore Tesla V100 da 16 Gb con 4 core

Abbiamo installato Ubuntu 18.04 sul nostro computer.

Impostazione dell'ambiente

Ora installiamo tutto il necessario per il lavoro sul server. Poiché il nostro articolo è principalmente per principianti, parlerò di alcuni punti che saranno loro utili.

Gran parte del lavoro durante la configurazione di un ambiente viene svolto tramite la riga di comando. La maggior parte degli utenti utilizza Windows come sistema operativo funzionante. La console standard in questo sistema operativo lascia molto a desiderare. Pertanto, utilizzeremo uno strumento conveniente Comandante/. Scarica la versione mini ed esegui Cmder.exe. Successivamente è necessario connettersi al server tramite SSH:

ssh root@server-ip-or-hostname

Invece di server-ip-or-hostname, specifica l'indirizzo IP o il nome DNS del tuo server. Successivamente, inserisci la password e se la connessione ha esito positivo, dovremmo ricevere un messaggio simile a questo.

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

Il linguaggio principale per lo sviluppo di modelli ML è Python. E la piattaforma più popolare per il suo utilizzo su Linux è Anaconda.

Installiamolo sul nostro server.

Iniziamo aggiornando il gestore pacchetti locale:

sudo apt-get update

Installa curl (utilità della riga di comando):

sudo apt-get install curl

Scarica l'ultima versione di Anaconda Distribution:

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

Iniziamo l'installazione:

bash Anaconda3-2019.10-Linux-x86_64.sh

Durante il processo di installazione ti verrà chiesto di confermare il contratto di licenza. Una volta completata l'installazione con successo dovresti vedere questo:

Thank you for installing Anaconda3!

Sono ormai molti i framework creati per lo sviluppo di modelli ML; noi lavoriamo con i più diffusi: PyTorch и tensorflow.

L'utilizzo del framework consente di aumentare la velocità di sviluppo e utilizzare strumenti già pronti per attività standard.

In questo esempio lavoreremo con PyTorch. Installiamolo:

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

Ora dobbiamo lanciare Jupyter Notebook, uno strumento di sviluppo popolare per gli specialisti di ML. Ti consente di scrivere codice e vedere immediatamente i risultati della sua esecuzione. Jupyter Notebook è incluso in Anaconda ed è già installato sul nostro server. È necessario connettersi ad esso dal nostro sistema desktop.

Per fare ciò, lanceremo prima Jupyter sul server specificando la porta 8080:

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

Successivamente, aprendo un'altra scheda nella nostra console Cmder (menu in alto - Finestra di dialogo Nuova console) ci collegheremo tramite la porta 8080 al server tramite SSH:

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

Quando inseriamo il primo comando, ci verranno offerti i collegamenti per aprire Jupyter nel nostro browser:

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

Usiamo il collegamento per localhost:8080. Copia il percorso completo e incollalo nella barra degli indirizzi del browser locale del tuo PC. Si aprirà il taccuino Jupyter.

Creiamo un nuovo notebook: Nuovo - Notebook - Python 3.

Controlliamo il corretto funzionamento di tutti i componenti che abbiamo installato. Inseriamo il codice PyTorch di esempio in Jupyter ed eseguiamo l'esecuzione (pulsante Esegui):

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

Il risultato dovrebbe essere qualcosa del genere:

La tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

Se ottieni un risultato simile, allora abbiamo configurato tutto correttamente e possiamo iniziare a sviluppare una rete neurale!

Creazione di una rete neurale

Creeremo una rete neurale per il riconoscimento delle immagini. Prendiamo questo come base guida.

Utilizzeremo il set di dati CIFAR10 disponibile pubblicamente per addestrare la rete. Ha classi: "aereo", "macchina", "uccello", "gatto", "cervo", "cane", "rana", "cavallo", "nave", "camion". Le immagini in CIFAR10 sono 3x32x32, ovvero immagini a colori a 3 canali di 32x32 pixel.

La tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti
Per lavoro utilizzeremo il pacchetto creato da PyTorch per lavorare con le immagini: torchvision.

Eseguiremo i seguenti passaggi in ordine:

  • Caricamento e normalizzazione di set di dati di training e test
  • Definizione di rete neurale
  • Addestramento di rete sui dati di addestramento
  • Test di rete sui dati di test
  • Ripetiamo l'addestramento e i test utilizzando la GPU

Eseguiremo tutto il codice seguente in Jupyter Notebook.

Caricamento e normalizzazione CIFAR10

Copia ed esegui il seguente codice in 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 risposta dovrebbe essere:

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

Visualizziamo diverse immagini di allenamento per i 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)))

La tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

Definizione di rete neurale

Consideriamo innanzitutto come funziona una rete neurale per il riconoscimento delle immagini. Questa è una semplice rete punto-punto. Prende i dati di input, li passa attraverso diversi livelli uno per uno e infine produce i dati di output.

La tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

Creiamo una rete simile nel nostro ambiente:


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

Definiamo anche una funzione di perdita e un ottimizzatore


import torch.optim as optim

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

Addestramento di rete sui dati di addestramento

Iniziamo ad allenare la nostra rete neurale. Tieni presente che dopo aver eseguito questo codice, dovrai attendere un po' di tempo fino al completamento del lavoro. Mi ci sono voluti 5 minuti. Ci vuole tempo per addestrare la rete.

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

Otteniamo il seguente risultato:

La tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

Salviamo il nostro modello addestrato:

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

Test di rete sui dati di test

Abbiamo addestrato la rete utilizzando una serie di dati di addestramento. Ma dobbiamo verificare se la rete ha imparato qualcosa.

Lo testeremo prevedendo l'etichetta della classe restituita dalla rete neurale e testandola per vedere se è vera. Se la previsione è corretta, aggiungiamo il campione all'elenco delle previsioni corrette.
Mostriamo un'immagine dal set di 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)))

La tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

Ora chiediamo alla rete neurale di dirci cosa c'è in queste immagini:


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 tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

I risultati sembrano piuttosto buoni: la rete ha identificato correttamente tre immagini su quattro.

Vediamo come si comporta la rete nell'intero set di dati.


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 tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

Sembra che la rete sappia qualcosa e funzioni. Se determinasse le classi in modo casuale, la precisione sarebbe del 10%.

Vediamo ora quali classi la rete identifica meglio:

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 tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

Sembra che la rete sia la migliore nell'identificare auto e navi: precisione del 71%.

Quindi la rete funziona. Ora proviamo a trasferire il suo lavoro al processore grafico (GPU) e vediamo cosa cambia.

Addestramento di una rete neurale su GPU

Per prima cosa spiegherò brevemente cos’è CUDA. CUDA (Compute Unified Device Architecture) è una piattaforma di elaborazione parallela sviluppata da NVIDIA per l'elaborazione generale su unità di elaborazione grafica (GPU). Con CUDA, gli sviluppatori possono accelerare notevolmente le applicazioni informatiche sfruttando la potenza delle GPU. Questa piattaforma è già installata sul nostro server che abbiamo acquistato.

Per prima cosa definiamo la nostra GPU come il primo dispositivo cuda visibile.

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 tua prima rete neurale su un'unità di elaborazione grafica (GPU). Guida per principianti

Invio della rete alla GPU:

net.to(device)

Dovremo anche inviare input e target ad ogni passaggio alla GPU:

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

Riqualificamo la rete sulla 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')

Questa volta l'allenamento in rete è durato circa 3 minuti. Ricordiamo che la stessa fase su un processore convenzionale è durata 5 minuti. La differenza non è significativa, questo accade perché la nostra rete non è così grande. Quando si utilizzano array di grandi dimensioni per l'addestramento, la differenza tra la velocità della GPU e quella di un processore tradizionale aumenterà.

Questo sembra essere tutto. Cosa siamo riusciti a fare:

  • Abbiamo esaminato cos'è una GPU e selezionato il server su cui è installata;
  • Abbiamo predisposto un ambiente software per creare una rete neurale;
  • Abbiamo creato una rete neurale per il riconoscimento delle immagini e l'abbiamo addestrata;
  • Abbiamo ripetuto l'addestramento della rete utilizzando la GPU e abbiamo riscontrato un aumento di velocità.

Sarò felice di rispondere alle domande nei commenti.

Fonte: habr.com

Aggiungi un commento