Sua primeira rede neural em uma unidade de processamento gráfico (GPU). Guia do iniciante


Neste artigo, mostrarei como configurar um ambiente de aprendizado de máquina em 30 minutos, criar uma rede neural para reconhecimento de imagens e executar a mesma rede em uma unidade de processamento gráfico (GPU).

Primeiro, vamos definir o que é uma rede neural.

No nosso caso, esse é um modelo matemático, bem como sua implementação de software ou hardware, construída com base no princípio de organização e funcionamento de redes neurais biológicas - redes de células nervosas de um organismo vivo. Esse conceito surgiu no estudo de processos que ocorrem no cérebro e na tentativa de simular esses processos.

As redes neurais não são programadas no sentido usual da palavra, elas são treinadas. A capacidade de aprendizado é uma das principais vantagens das redes neurais em relação aos algoritmos tradicionais. Tecnicamente, o treinamento consiste em encontrar os coeficientes de conexões entre os neurônios. No processo de aprendizagem, a rede neural é capaz de identificar relações complexas entre entrada e saída, além de realizar generalizações.

Do ponto de vista do aprendizado de máquina, uma rede neural é um caso especial de métodos de reconhecimento de padrões, análise discriminante, métodos de agrupamento e outros métodos.

Equipamento


Primeiro, vamos lidar com o equipamento. Precisamos de um servidor com o sistema operacional Linux instalado. O equipamento para a operação de sistemas de aprendizado de máquina requer uma capacidade suficientemente poderosa e, como conseqüência, dispendiosa. Para aqueles que não têm um bom carro em mãos, recomendo prestar atenção à oferta dos provedores de nuvem. O servidor necessário pode ser alugado rapidamente e pagado apenas pelo tempo de uso.

Nos projetos em que é necessário criar redes neurais, eu uso os servidores de um dos provedores de nuvem russos. A empresa oferece servidores de aluguel de nuvem especificamente para aprendizado de máquina com as poderosas unidades de processamento gráfico (GPUs) Tesla V100 da NVIDIA. Em resumo: o uso de um servidor com uma GPU pode ser dezenas de vezes mais eficiente (rápido) em comparação com um servidor com custo semelhante e que usa uma CPU (um processador central conhecido) para cálculos. Isso é alcançado devido às especificidades da arquitetura da GPU, que lida com os cálculos mais rapidamente.

Para executar os exemplos descritos abaixo, adquirimos o seguinte servidor por vários dias:

  • SSD de 150 GB
  • RAM 32 GB
  • Processador Tesla V100 de 16 Gb com 4 núcleos

O Ubuntu 18.04 foi instalado na máquina.

Defina o ambiente


Agora instale no servidor tudo o que você precisa para funcionar. Como nosso artigo é principalmente para iniciantes, falarei sobre alguns pontos que serão úteis para eles.

Muito trabalho ao configurar o ambiente é feito através da linha de comando. A maioria dos usuários usa o Windows como um sistema operacional ativo. O console padrão neste sistema operacional deixa muito a desejar. Portanto, usaremos a conveniente Cmder / ferramenta . Baixe a versão mini e execute o Cmder.exe. Em seguida, você precisa se conectar ao servidor via SSH:

ssh root@server-ip-or-hostname

Em vez de server-ip-or-hostname, especifique o endereço IP ou o nome DNS do seu servidor. Em seguida, digite a senha e, após uma conexão bem-sucedida, devemos obter algo parecido com isto.

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

A principal linguagem para o desenvolvimento de modelos de ML é o Python. E a plataforma mais popular para usá-lo no Linux é o Anaconda .

Instale-o em nosso servidor.

Começamos atualizando o gerenciador de pacotes local:

sudo apt-get update

Instale o curl (utilitário de linha de comando):

sudo apt-get install curl

Faça o download da versão mais recente do Anaconda Distribution:

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

Iniciamos a instalação:

bash Anaconda3-2019.10-Linux-x86_64.sh

Durante o processo de instalação, você precisará confirmar o contrato de licença. Na instalação bem-sucedida, você deve ver o seguinte:

Thank you for installing Anaconda3!

Para desenvolver modelos de ML, muitas estruturas foram criadas, trabalhamos com as mais populares: PyTorch e Tensorflow .

O uso da estrutura permite aumentar a velocidade do desenvolvimento e usar ferramentas prontas para tarefas padrão.

Neste exemplo, trabalharemos com o PyTorch. Instale-o:

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

Agora precisamos lançar o Jupyter Notebook - uma ferramenta de desenvolvimento popular entre os especialistas em ML. Permite escrever código e ver imediatamente os resultados de sua execução. O Jupyter Notebook faz parte do Anaconda e já está instalado em nosso servidor. Você precisa se conectar a ele no nosso sistema de desktop.

Para fazer isso, primeiro executamos o Jupyter no servidor especificando a porta 8080:

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

Em seguida, abrindo outra guia no console do Cmder (o menu superior é a caixa de diálogo Novo console), conecte na porta 8080 ao servidor via SSH:

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

Quando você digita o primeiro comando, serão oferecidos links para abrir o Jupyter em nosso 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

Use o link para localhost: 8080. Copie o caminho completo e cole na barra de endereços do navegador local do seu PC. O Caderno Jupyter é aberto.

Vamos criar um novo laptop: Novo - Notebook - Python 3.

Verifique a operação correta de todos os componentes que instalamos. Introduzimos um exemplo de código PyTorch no Jupyter e iniciamos a execução (botão Executar):

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

O resultado deve ser algo assim:



Se você tiver um resultado semelhante, todos nós configuramos corretamente e podemos começar a desenvolver uma rede neural!

Crie uma rede neural


Criaremos uma rede neural para reconhecimento de imagem. Tomamos este guia como base .

Para treinar a rede, usaremos o conjunto de dados CIFAR10 disponível ao público. Ele tem aulas: “avião”, “carro”, “pássaro”, “gato”, “cervo”, “cachorro”, “sapo”, “cavalo”, “navio”, “caminhão”. As imagens no CIFAR10 têm um tamanho de 3x32x32, ou seja, imagens coloridas de 3 canais de 32x32 pixels.


Para o trabalho, usaremos o pacote PyTorch criado para trabalhar com imagens - torchvision.

Seguiremos as seguintes etapas em ordem:

  • Baixe e normalize conjuntos de dados de treinamento e teste
  • Definição de rede neural
  • Treinamento em rede sobre dados de treinamento
  • Testando a rede com dados de teste
  • Repita o treinamento e teste da GPU

Todo o código abaixo iremos executar no Jupyter Notebook.

Baixe e normalize o CIFAR10


Copie e execute o seguinte código no 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')

A resposta deve ser assim:

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

Obteremos várias imagens de treinamento para verificação:


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



Definição de rede neural


Vamos primeiro examinar como funciona uma rede neural para reconhecimento de imagens. Esta é uma rede de conexão direta simples. Ele recebe entrada, passa por várias camadas, uma a uma e, finalmente, fornece a saída.



Vamos criar uma rede semelhante em nosso 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()

Também definimos a função de perda e o otimizador


import torch.optim as optim

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

Treinamento em rede sobre dados de treinamento


Começamos a treinar nossa rede neural. Chamo a atenção para o fato de que, depois disso, ao executar esse código, você precisará esperar um pouco até que o trabalho seja concluído. Levei 5 minutos. Rede leva tempo.

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

Obtemos o seguinte resultado:



Salvamos nosso modelo treinado:

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

Testando a rede com dados de teste


Nós treinamos a rede usando um conjunto de dados de treinamento. Mas precisamos verificar se a rede aprendeu alguma coisa.

Verificaremos isso prevendo o rótulo da classe que a rede neural gera e verificando a verdade. Se a previsão estiver correta, adicionamos a amostra à lista de previsões corretas.
Vamos mostrar a imagem da suíte de testes:

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



Agora peça à rede neural para nos dizer o que há nessas figuras:


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



Os resultados parecem muito bons: a rede identificou corretamente três das quatro fotos.

Vamos ver como a rede funciona em todo o conjunto de dados.


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



Parece que a rede conhece e funciona. Se ele definisse as aulas aleatoriamente, a precisão seria de 10%.

Agora vamos ver quais classes a rede define melhor:

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



Parece que a rede determina melhor os carros e os navios: 71% de precisão.

Então a rede está funcionando. Agora vamos tentar transferir seu trabalho para o processador gráfico (GPU) e ver o que muda.

Treinamento em rede neural da GPU


Primeiro, explicarei brevemente o que é CUDA. CUDA (Compute Unified Device Architecture) é uma plataforma de computação paralela desenvolvida pela NVIDIA para computação geral em GPUs. Com o CUDA, os desenvolvedores podem acelerar significativamente os aplicativos de computação usando os recursos das GPUs. No servidor que compramos, essa plataforma já está instalada.

Vamos primeiro definir nossa GPU como o primeiro dispositivo cuda visível.

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 )



Envie a rede para a GPU:

net.to(device)

Também teremos que enviar insumos e metas a cada passo e para a GPU:

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

Execute a reciclagem da rede já na 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')

Desta vez, o treinamento em rede durou cerca de 3 minutos. Lembre-se de que o mesmo estágio em um processador regular durou 5 minutos. A diferença não é significativa, isso ocorre porque nossa rede não é tão grande. Ao usar matrizes grandes para treinamento, a diferença entre a velocidade da GPU e o processador tradicional aumentará.

Isso parece ser tudo. O que conseguimos fazer:

  • Examinamos o que é a GPU e escolhemos o servidor em que está instalada;
  • Criamos um ambiente de software para criar uma rede neural;
  • Criamos uma rede neural para reconhecimento de imagens e a treinamos;
  • Repetimos o treinamento da rede usando a GPU e recebemos um aumento na velocidade.

Ficarei feliz em responder às perguntas nos comentários.

All Articles