Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes
En este artículo, le diré cómo configurar un entorno de aprendizaje automático en 30 minutos, crear una red neuronal para el reconocimiento de imágenes y luego ejecutar la misma red en un procesador de gráficos (GPU).

Primero, definamos qué es una red neuronal.

En nuestro caso, se trata de un modelo matemático, así como su realización de software o hardware, basado en el principio de organización y funcionamiento de las redes neuronales biológicas, las redes de células nerviosas de un organismo vivo. Este concepto surgió al estudiar los procesos que ocurren en el cerebro y al intentar modelarlos.

Las redes neuronales no se programan en el sentido habitual de la palabra, sino que se entrenan. La capacidad de aprender es una de las principales ventajas de las redes neuronales frente a los algoritmos tradicionales. Técnicamente, el aprendizaje consiste en encontrar los coeficientes de conexiones entre neuronas. Durante el proceso de entrenamiento, la red neuronal puede identificar dependencias complejas entre los datos de entrada y los datos de salida, así como realizar una generalización.

Desde el punto de vista del aprendizaje automático, una red neuronal es un caso especial de métodos de reconocimiento de patrones, análisis discriminante, métodos de agrupamiento y otros métodos.

Equipo

Primero, veamos el equipo. Necesitamos un servidor con el sistema operativo Linux instalado. El equipo necesario para operar sistemas de aprendizaje automático es bastante potente y, como resultado, costoso. Para aquellos que no tengan una buena máquina a mano, recomiendo prestar atención a las ofertas de los proveedores de la nube. Puede alquilar el servidor requerido rápidamente y pagar solo por el tiempo de uso.

En proyectos donde es necesario crear redes neuronales, utilizo los servidores de uno de los proveedores de nube rusos. La empresa ofrece alquiler de servidores en la nube específicos para el aprendizaje automático con potentes procesadores gráficos (GPU) Tesla V100 de NVIDIA. En resumen: utilizar un servidor con GPU puede ser decenas de veces más eficiente (rápido) en comparación con un servidor de coste similar que utiliza una CPU (la conocida unidad central de procesamiento) para los cálculos. Esto se logra gracias a las características de la arquitectura GPU, que hace frente a los cálculos más rápidamente.

Para implementar los ejemplos que se describen a continuación, compramos el siguiente servidor por varios días:

  • Disco SSD de 150GB
  • RAM 32GB
  • Procesador Tesla V100 de 16 Gb con 4 núcleos

Instalamos Ubuntu 18.04 en nuestra máquina.

Configurando el ambiente

Ahora instalemos todo lo necesario para trabajar en el servidor. Dado que nuestro artículo está dirigido principalmente a principiantes, hablaré sobre algunos puntos que les serán útiles.

Gran parte del trabajo a la hora de configurar un entorno se realiza a través de la línea de comandos. La mayoría de los usuarios utilizan Windows como sistema operativo de trabajo. La consola estándar de este sistema operativo deja mucho que desear. Por lo tanto, utilizaremos una herramienta conveniente. Comandante/. Descargue la versión mini y ejecute Cmder.exe. Lo siguiente que necesitas es conectarte al servidor vía SSH:

ssh root@server-ip-or-hostname

En lugar de server-ip-or-hostname, especifique la dirección IP o el nombre DNS de su servidor. A continuación, ingresamos la contraseña y si la conexión es exitosa, deberíamos recibir un mensaje similar a este.

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

El lenguaje principal para desarrollar modelos ML es Python. Y la plataforma más popular para su uso en Linux es Anaconda.

Instalémoslo en nuestro servidor.

Empezamos actualizando el administrador de paquetes local:

sudo apt-get update

Instale curl (utilidad de línea de comando):

sudo apt-get install curl

Descargue la última versión de Anaconda Distribution:

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

Comencemos la instalación:

bash Anaconda3-2019.10-Linux-x86_64.sh

Durante el proceso de instalación, se le pedirá que confirme el acuerdo de licencia. Tras una instalación exitosa, debería ver esto:

Thank you for installing Anaconda3!

Se han creado muchos marcos para el desarrollo de modelos de ML; trabajamos con los más populares: PyTorch и Flujo tensor.

El uso del marco le permite aumentar la velocidad de desarrollo y utilizar herramientas listas para usar para tareas estándar.

En este ejemplo trabajaremos con PyTorch. Instalémoslo:

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

Ahora necesitamos lanzar Jupyter Notebook, una popular herramienta de desarrollo para especialistas en ML. Le permite escribir código y ver inmediatamente los resultados de su ejecución. Jupyter Notebook está incluido con Anaconda y ya está instalado en nuestro servidor. Debe conectarse a él desde nuestro sistema de escritorio.

Para hacer esto, primero iniciaremos Jupyter en el servidor especificando el puerto 8080:

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

A continuación, abriendo otra pestaña en nuestra consola cmder (menú superior – Diálogo Nueva consola) nos conectaremos mediante el puerto 8080 al servidor vía SSH:

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

Cuando ingresamos el primer comando, se nos ofrecerán enlaces para abrir Jupyter en nuestro 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

Usemos el enlace para localhost:8080. Copie la ruta completa y péguela en la barra de direcciones del navegador local de su PC. Se abrirá Jupyter Notebook.

Creemos un nuevo cuaderno: Nuevo - Cuaderno - Python 3.

Comprobemos el correcto funcionamiento de todos los componentes que instalamos. Ingresemos el código PyTorch de ejemplo en Jupyter y ejecutemos la ejecución (botón Ejecutar):

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

El resultado debería ser algo como esto:

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Si obtiene un resultado similar, entonces hemos configurado todo correctamente y ¡podemos comenzar a desarrollar una red neuronal!

Creando una red neuronal

Crearemos una red neuronal para el reconocimiento de imágenes. Tomemos esto como base. руководство.

Utilizaremos el conjunto de datos CIFAR10 disponible públicamente para entrenar la red. Tiene clases: “avión”, “coche”, “pájaro”, “gato”, “ciervo”, “perro”, “rana”, “caballo”, “barco”, “camión”. Las imágenes en CIFAR10 son de 3x32x32, es decir, imágenes en color de 3 canales de 32x32 píxeles.

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes
Para trabajar, usaremos el paquete creado por PyTorch para trabajar con imágenes: torchvision.

Realizaremos los siguientes pasos en orden:

  • Carga y normalización de conjuntos de datos de prueba y entrenamiento
  • Definición de red neuronal
  • Entrenamiento de red sobre datos de entrenamiento.
  • Pruebas de red en datos de prueba.
  • Repitamos el entrenamiento y las pruebas usando GPU.

Ejecutaremos todo el código a continuación en Jupyter Notebook.

Cargando y normalizando CIFAR10

Copie y ejecute el siguiente código en 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 respuesta debería 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

Mostremos varias imágenes de entrenamiento para realizar pruebas:


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

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Definición de red neuronal

Consideremos primero cómo funciona una red neuronal para el reconocimiento de imágenes. Esta es una red simple punto a punto. Toma datos de entrada, los pasa a través de varias capas una por una y finalmente produce datos de salida.

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Creemos una red similar en nuestro entorno:


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

También definimos una función de pérdida y un optimizador.


import torch.optim as optim

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

Entrenamiento de red sobre datos de entrenamiento.

Comencemos a entrenar nuestra red neuronal. Tenga en cuenta que después de ejecutar este código, deberá esperar un tiempo hasta que se complete el trabajo. Me tomó 5 minutos. Se necesita tiempo para entrenar la red.

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

Obtenemos el siguiente resultado:

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Guardamos nuestro modelo entrenado:

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

Pruebas de red en datos de prueba.

Entrenamos la red utilizando un conjunto de datos de entrenamiento. Pero necesitamos comprobar si la red ha aprendido algo.

Probaremos esto prediciendo la etiqueta de clase que genera la red neuronal y probándola para ver si es cierta. Si la predicción es correcta, agregamos la muestra a la lista de predicciones correctas.
Mostremos una imagen del conjunto de prueba:

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

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Ahora pidámosle a la red neuronal que nos diga qué hay en estas imágenes:


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

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Los resultados parecen bastante buenos: la red identificó correctamente tres de cada cuatro imágenes.

Veamos cómo se desempeña la red en todo el conjunto de datos.


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

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Parece que la red sabe algo y está funcionando. Si determinara las clases al azar, la precisión sería del 10%.

Ahora veamos qué clases identifica mejor la red:

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

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Parece que la red es mejor para identificar automóviles y barcos: 71% de precisión.

Entonces la red está funcionando. Ahora intentemos transferir su trabajo al procesador gráfico (GPU) y veamos qué cambia.

Entrenando una red neuronal en GPU

Primero, explicaré brevemente qué es CUDA. CUDA (Compute Unified Device Architecture) es una plataforma informática paralela desarrollada por NVIDIA para informática general en unidades de procesamiento de gráficos (GPU). Con CUDA, los desarrolladores pueden acelerar drásticamente las aplicaciones informáticas aprovechando el poder de las GPU. Esta plataforma ya está instalada en nuestro servidor que compramos.

Primero definamos nuestra GPU como el primer dispositivo 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 )

Su primera red neuronal en una unidad de procesamiento de gráficos (GPU). Guía para principiantes

Envío de la red a la GPU:

net.to(device)

También tendremos que enviar entradas y objetivos en cada paso a la GPU:

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

Volvamos a entrenar la red en 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')

Esta vez, el entrenamiento en red duró unos 3 minutos. Recordemos que la misma etapa en un procesador convencional duró 5 minutos. La diferencia no es significativa, esto sucede porque nuestra red no es tan grande. Cuando se utilizan matrices grandes para el entrenamiento, aumentará la diferencia entre la velocidad de la GPU y la de un procesador tradicional.

Eso parece ser todo. Lo que logramos hacer:

  • Miramos qué es una GPU y seleccionamos el servidor en el que está instalada;
  • Hemos configurado un entorno de software para crear una red neuronal;
  • Creamos una red neuronal para el reconocimiento de imágenes y la entrenamos;
  • Repetimos el entrenamiento de red usando la GPU y obtuvimos un aumento en la velocidad.

Estaré encantado de responder preguntas en los comentarios.

Fuente: habr.com

Añadir un comentario