
In questo articolo, spiegherò come configurare in 30 minuti un ambiente per l'apprendimento automatico, creare una rete neurale per il riconoscimento delle immagini e poi eseguire la stessa rete su una scheda grafica (GPU).
Per iniziare, definiamo cos'è una rete neurale.
Nel nostro caso, si tratta di un modello matematico, così come della sua incarnazione software o hardware, costruito secondo il principio di organizzazione e funzionamento delle reti neurali biologiche — reti di cellule nervose di un organismo vivente. Questo concetto è emerso dallo studio dei processi che avvengono nel cervello e dal tentativo di modellare tali processi.
Le reti neurali non vengono programmate nel senso tradizionale del termine, ma vengono addestrate. La possibilità di apprendimento è uno dei principali vantaggi delle reti neurali rispetto agli algoritmi tradizionali. Tecnicamente, l'apprendimento consiste nella determinazione dei coefficienti di connessione tra i neuroni. Durante il processo di apprendimento, la rete neurale è in grado di identificare complesse dipendenze tra i dati di input e quelli di output, oltre a effettuare generalizzazioni.
Dal punto di vista dell'apprendimento automatico, una rete neurale rappresenta un caso particolare dei metodi di riconoscimento dei modelli, analisi discriminante, metodi di clustering e altri.
Apparecchiatura
Iniziamo con l'hardware. Abbiamo bisogno di un server con un sistema operativo Linux installato. L'hardware necessario per il funzionamento dei sistemi di apprendimento automatico deve essere piuttosto potente e, di conseguenza, costoso. A chi non dispone di una buona macchina consiglio di prestare attenzione alle offerte dei provider cloud. È possibile affittare rapidamente il server necessario e pagare solo per il tempo di utilizzo.
Nei progetti in cui è necessario creare reti neurali, utilizzo server di uno dei provider cloud russi. L'azienda offre server cloud in affitto appositamente per l'apprendimento automatico con potenti schede grafiche (GPU) Tesla V100 di NVIDIA. In breve: utilizzare un server con GPU può essere decine di volte più efficiente (veloce) rispetto a un server di costo equivalente in cui si utilizza la CPU (il ben noto processore centrale). Ciò è reso possibile dalle caratteristiche dell'architettura GPU, che gestisce i calcoli più rapidamente.
Per eseguire gli esempi descritti di seguito, abbiamo noleggiato per alcuni giorni un server del genere:
- Disco SSD da 150 GB
- RAM da 32 GB
- Processore Tesla V100 16 Gb con 4 core
Sul server è stata installata Ubuntu 18.04.
Impostiamo l'ambiente
Ora installeremo sul server tutto il necessario per il lavoro. Poiché il nostro articolo è principalmente destinato ai principianti, parlerò di alcuni aspetti che saranno utili a loro.
Gran parte del lavoro nella configurazione dell'ambiente viene eseguita tramite la riga di comando. La maggior parte degli utenti utilizza Windows come sistema operativo di lavoro. Il terminale di questa OS lascia a desiderare. Pertanto, utilizzeremo uno strumento comodo . Scarichiamo la versione mini e avviamo Cmder.exe. Successivamente, dobbiamo connetterci al server utilizzando il protocollo SSH:
ssh root@server-ip-or-hostnameAl posto di server-ip-or-hostname specifica l'indirizzo IP o il nome DNS del tuo server. Poi inserisci la password e, in caso di successo, dovremmo ricevere un messaggio simile.
Benvenuto in Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-74-generic x86_64)Il linguaggio principale per lo sviluppo dei modelli di ML è Python. La piattaforma più popolare per utilizzarlo su Linux è .
Installa sul nostro server.
Iniziamo aggiornando il gestore dei pacchetti locale:
sudo apt-get updateInstalliamo curl (strumento da linea di comando):
sudo apt-get install curlScarichiamo l'ultima versione della distribuzione Anaconda:
cd /tmp
curl –O https://repo.anaconda.com/archive/Anaconda3-2019.10-Linux-x86_64.shIniziamo l'installazione:
bash Anaconda3-2019.10-Linux-x86_64.shDurante l'installazione sarà necessario confermare il contratto di licenza. Una volta completata l'installazione dovresti vedere questo:
Grazie per aver installato Anaconda3!Attualmente sono stati creati molti framework per lo sviluppo di modelli di ML, noi lavoriamo con i più popolari: e .
L'uso del framework consente di aumentare la velocità di sviluppo e di utilizzare strumenti già pronti per compiti standard.
In questo esempio lavoreremo con PyTorch. Installiamolo:
conda install pytorch torchvision cudatoolkit=10.1 -c pytorchOra dobbiamo avviare Jupyter Notebook, uno strumento di sviluppo molto popolare tra i professionisti del ML. Consente di scrivere codice e vedere immediatamente i risultati della sua esecuzione. Jupyter Notebook fa parte di Anaconda ed è già installato sul nostro server. È necessario connettersi da sistema desktop.
Per questo, prima avvieremo Jupyter sul server specificando la porta 8080:
jupyter notebook --no-browser --port=8080 --allow-rootSuccessivamente, aprendo un'altra scheda nella nostra console Cmder (menu superiore — New console dialog) ci connetteremo al server tramite SSH sulla porta 8080:
ssh -L 8080:localhost:8080 root@server-ip-or-hostnameQuando inseriremo il primo comando, ci verranno fornite le link 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
Utilizzeremo il link per localhost:8080. Copia il percorso completo e incollalo nella barra degli indirizzi del browser locale del tuo PC. Si aprirà Jupyter Notebook.
Creeremo un nuovo notebook: New — Notebook — Python 3.
Verificheremo il corretto funzionamento di tutti i componenti che abbiamo installato. Inseriamo in Jupyter un esempio di codice PyTorch e avviamo l'esecuzione (pulsante Run):
from __future__ import print_function
import torch
x = torch.rand(5, 3)
print(x)
Il risultato dovrebbe essere simile a questo:

Se hai un risultato simile, significa che abbiamo impostato tutto correttamente e possiamo iniziare a sviluppare una rete neurale!
Creiamo una rete neurale
Creeremo una rete neurale per il riconoscimento delle immagini. Prenderemo come base questo .
Per addestrare la rete utilizzeremo un set di dati pubblico, il CIFAR10. Quest'ultimo contiene classi: "aereo", "automobile", "uccello", "gatto", "cervo", "cane", "rana", "cavallo", "nave", "camion". Le immagini in CIFAR10 hanno una dimensione di 3x32x32, ovvero immagini a colori a 3 canali di dimensioni 32×32 pixel.

Per il lavoro utilizzeremo il pacchetto PyTorch creato per gestire le immagini — torchvision.
Seguiremo i seguenti passaggi in ordine:
- Caricamento e normalizzazione dei set di dati di addestramento e test
- Definizione della rete neurale
- Addestramento della rete sui dati di addestramento
- Test della rete sui dati di test
- Ripeteremo l'addestramento e il test utilizzando la GPU
Tutto il codice fornito qui sotto lo eseguiremo in Jupyter Notebook.
Caricamento e normalizzazione del CIFAR10
Copia e incolla in Jupyter il seguente codice:
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)
classi = ('aereo', 'auto', 'uccello', 'gatto',
'cerbiatto', 'cane', 'rana', 'cavallo', 'nave', 'camion')La risposta deve essere così:
Scaricando https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz in ./data/cifar-10-python.tar.gz
Estraendo ./data/cifar-10-python.tar.gz in ./data
File già scaricati e verificatiMostriamo alcune immagini di addestramento per il controllo:
import matplotlib.pyplot as plt
import numpy as np
# funzioni per mostrare un'immagine
def imshow(img):
img = img / 2 + 0.5 # denormalizza
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
# otteniamo alcune immagini di addestramento casuali
dataiter = iter(trainloader)
images, labels = dataiter.next()
# mostra immagini
imshow(torchvision.utils.make_grid(images))
# stampa etichette
print(' '.join('%5s' % classi[labels[j]] for j in range(4)))

Definizione della rete neurale
Cominciamo a vedere come funziona una rete neurale per il riconoscimento delle immagini. È una rete semplice a connessione diretta. Prende i dati in ingresso, li passa attraverso più strati uno dopo l'altro e, infine, produce i dati in uscita.

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 la funzione di perdita e l'ottimizzatore
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)Addestramento della rete sui dati di addestramento
Iniziamo l'addestramento della nostra rete neurale. Faccio notare che dopo aver avviato questo codice, dovrete aspettare un po' di tempo fino al termine dell'esecuzione. A me è durata 5 minuti. L'addestramento della rete richiede tempo.
per epoch in range(2): # ciclo sui dati più volte
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# ottieni gli input; data è una lista di [inputs, labels]
inputs, labels = data
# azzera i gradienti dei parametri
optimizer.zero_grad()
# avanti + indietro + ottimizza
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# stampa statistiche
running_loss += loss.item()
if i % 2000 == 1999: # stampa ogni 2000 mini-batch
print('[%d, ] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Addestramento completato')
Otterremo il seguente risultato:

Salviamo il nostro modello addestrato:
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)Test della rete sui dati di test
Abbiamo addestrato la rete utilizzando un insieme di dati di addestramento. Ma dobbiamo verificare se la rete ha effettivamente appreso qualcosa.
Verificheremo questo, prevedendo l'etichetta di classe che restituisce la rete neurale e controllandola per verificarne la correttezza. Se la previsione è corretta, aggiungiamo il campione alla lista delle previsioni corrette.
Mostriamo un'immagine dall'insieme di test:
dataiter = iter(testloader)
images, labels = dataiter.next()
# stampa le immagini
imshow(torchvision.utils.make_grid(images))
print('Verità a terra: ', ' '.join('%5s' % classes[labels[j]] for j in range(4))) 
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('Predetto: ', ' '.join('%5s' % classes[predicted[j]]
for j in range(4)))

I risultati sembrano piuttosto buoni: la rete ha identificato correttamente tre immagini su quattro.
Vediamo come si comporta la rete sull'intero insieme di dati.
corretto = 0
totale = 0
con torch.no_grad():
for dati in testloader:
images, labels = dati
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
totale += labels.size(0)
corretto += (predicted == labels).sum().item()
print('Accuratezza della rete sulle 10000 immagini di test: %d %%' % (
100 * corretto / totale)) 
Sembra che la rete sappia qualcosa e funzioni. Se avesse identificato le classi a caso, l'accuratezza sarebbe stata del 10%.
Ora vediamo 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('Accuratezza di %5s : %%' % (
classes[i], 100 * class_correct[i] / class_total[i])) 
Sembra che la rete identifichi meglio automobili e navi: 71% di accuratezza.
Quindi la rete funziona. Ora proviamo a trasferire il suo funzionamento su una scheda grafica (GPU) e vediamo cosa cambia.
Addestramento della rete neurale su GPU
Iniziamo con una breve spiegazione di cos'è CUDA. CUDA (Compute Unified Device Architecture) è una piattaforma di calcolo parallelo sviluppata da NVIDIA per l'esecuzione di calcoli generali su schede grafiche (GPU). Con CUDA, gli sviluppatori possono accelerare in modo significativo le applicazioni di calcolo sfruttando le capacità delle schede grafiche. Sul nostro server, che abbiamo acquistato, questa piattaforma è già installata.
Prima di tutto, definiamo la nostra GPU come il primo dispositivo CUDA visibile.
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Assumendo che siamo su una macchina CUDA, questo dovrebbe stampare un dispositivo CUDA:
print(device) 
Invia la rete sulla GPU:
net.to(device)Dovremo anche inviare gli input e i target a ogni passaggio nella GPU:
inputs, labels = data[0].to(device), data[1].to(device)Avviamo il riaddestramento della rete già 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): # ciclo sui dati più volte
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# ottieni gli input; data è una lista di [inputs, labels]
inputs, labels = data[0].to(device), data[1].to(device)
# azzera i gradienti dei parametri
optimizer.zero_grad()
# avanti + indietro + ottimizza
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# stampa statistiche
running_loss += loss.item()
if i % 2000 == 1999: # stampa ogni 2000 mini-batch
print('[%d, ] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Addestramento completato')Questa volta l'allenamento della rete è durato circa 3 minuti. Ricordiamo che la stessa fase su una normale CPU è durata 5 minuti. La differenza non è significativa, poiché la nostra rete non è così grande. Con l'uso di grandi set di dati per l'addestramento, la differenza tra la velocità della GPU e quella della CPU tradizionale aumenterà.
Sembra che questo sia tutto. Ecco cosa siamo riusciti a fare:
- Abbiamo esaminato cosa è una GPU e scelto il server su cui è installata;
- Abbiamo configurato l'ambiente programmativo per creare una rete neurale;
- Abbiamo creato una rete neurale per il riconoscimento delle immagini e l'abbiamo addestrata;
- Abbiamo ripetuto l'allenamento della rete utilizzando la GPU e abbiamo ottenuto un aumento della velocità.
Sarò felice di rispondere a domande nei commenti.
Fonte: habr.com
