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

Sua primeira rede neural em uma unidade de processamento gráfico (GPU). Guia do iniciante
Neste artigo, direi como configurar um ambiente de aprendizado de máquina em 30 minutos, criar uma rede neural para reconhecimento de imagem e, em seguida, executar a mesma rede em um processador gráfico (GPU).

Primeiro, vamos definir o que é uma rede neural.

No nosso caso, trata-se de um modelo matemático, bem como de sua concretização em software ou hardware, construído com base no princípio de organização e funcionamento de redes neurais biológicas - redes de células nervosas de um organismo vivo. Este conceito surgiu enquanto estudava os processos que ocorrem no cérebro e tentava modelar esses processos.

As redes neurais não são programadas no sentido usual da palavra, elas são treinadas. A capacidade de aprender é uma das principais vantagens das redes neurais em relação aos algoritmos tradicionais. Tecnicamente, o aprendizado consiste em encontrar os coeficientes de conexões entre os neurônios. Durante o processo de treinamento, a rede neural é capaz de identificar dependências complexas entre os dados de entrada e de saída, bem como 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.

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

Primeiro, vamos dar uma olhada no equipamento. Precisamos de um servidor com sistema operacional Linux instalado. O equipamento necessário para operar sistemas de aprendizado de máquina é bastante poderoso e, consequentemente, caro. Para quem não tem uma boa máquina em mãos, recomendo ficar atento às ofertas dos provedores de nuvem. Você pode alugar o servidor necessário rapidamente e pagar apenas pelo tempo de uso.

Em projetos onde é necessário criar redes neurais, utilizo os servidores de um dos provedores de nuvem russos. A empresa oferece servidores em nuvem para aluguel especificamente para aprendizado de máquina com poderosos processadores gráficos (GPU) Tesla V100 da NVIDIA. Resumindo: usar um servidor com GPU pode ser dezenas de vezes mais eficiente (rápido) comparado a um servidor de custo semelhante que utiliza uma CPU (a conhecida unidade central de processamento) para cálculos. Isso é conseguido devido aos recursos da arquitetura GPU, que faz cálculos com mais rapidez.

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

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

Instalamos o Ubuntu 18.04 em nossa máquina.

Configurando o ambiente

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

Muito do trabalho de configuração de um ambiente é feito por meio da linha de comando. A maioria dos usuários usa o Windows como sistema operacional de trabalho. O console padrão neste sistema operacional deixa muito a desejar. Portanto, usaremos uma ferramenta conveniente Cmder/. Baixe a versão mini e execute 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 nome DNS do seu servidor. A seguir, digite a senha e se a conexão for bem-sucedida, devemos receber uma mensagem semelhante a esta.

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

A principal linguagem para desenvolvimento de modelos de ML é Python. E a plataforma mais popular para uso no Linux é Jibóia.

Vamos instalá-lo em nosso servidor.

Começamos atualizando o gerenciador de pacotes local:

sudo apt-get update

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

sudo apt-get install curl

Baixe a versão mais recente do Anaconda Distribution:

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

Vamos iniciar a instalação:

bash Anaconda3-2019.10-Linux-x86_64.sh

Durante o processo de instalação, você será solicitado a confirmar o contrato de licença. Após a instalação bem-sucedida, você deverá ver isto:

Thank you for installing Anaconda3!

Muitas estruturas já foram criadas para o desenvolvimento de modelos de ML; trabalhamos com os mais populares: PyTorch и Tensorflow.

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

Neste exemplo trabalharemos com PyTorch. Vamos instalá-lo:

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

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

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

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

A seguir, abrindo outra aba em nosso console Cmder (menu superior - caixa de diálogo Novo console) nos conectaremos via porta 8080 ao servidor via SSH:

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

Ao inserir 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

Vamos usar o link para localhost:8080. Copie o caminho completo e cole-o na barra de endereço do navegador local do seu PC. O Jupyter Notebook será aberto.

Vamos criar um novo notebook: New - Notebook - Python 3.

Vamos verificar o correto funcionamento de todos os componentes que instalamos. Vamos inserir o código PyTorch de exemplo no Jupyter e executar 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:

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

Se você obtiver um resultado semelhante, então configuramos tudo corretamente e podemos começar a desenvolver uma rede neural!

Criando uma rede neural

Criaremos uma rede neural para reconhecimento de imagens. Vamos tomar isso como base orientar.

Usaremos o conjunto de dados CIFAR10 disponível publicamente para treinar a rede. Possui aulas: “avião”, “carro”, “pássaro”, “gato”, “veado”, “cachorro”, “sapo”, “cavalo”, “navio”, “caminhão”. As imagens no CIFAR10 são 3x32x32, ou seja, imagens coloridas de 3 canais de 32x32 pixels.

Sua primeira rede neural em uma unidade de processamento gráfico (GPU). Guia do iniciante
Para o trabalho usaremos o pacote criado pelo PyTorch para trabalhar com imagens - torchvision.

Faremos as seguintes etapas em ordem:

  • Carregando e normalizando conjuntos de dados de treinamento e teste
  • Definição de Rede Neural
  • Treinamento de rede em dados de treinamento
  • Teste de rede em dados de teste
  • Vamos repetir o treinamento e o teste usando GPU

Estaremos executando todo o código abaixo no Jupyter Notebook.

Carregando e normalizando 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 deveria 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

Vamos exibir diversas imagens de treinamento para teste:


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

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

Definição de Rede Neural

Vamos primeiro considerar como funciona uma rede neural para reconhecimento de imagens. Esta é uma rede ponto a ponto simples. Ele pega os dados de entrada, passa por várias camadas, uma por uma, e finalmente produz os dados de saída.

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

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 uma função de perda e um otimizador


import torch.optim as optim

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

Treinamento de rede em dados de treinamento

Vamos começar a treinar nossa rede neural. Observe que depois de executar este código, você precisará esperar algum tempo até que o trabalho seja concluído. Levei 5 minutos. Leva tempo para treinar a rede.

 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:

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

Salvamos nosso modelo treinado:

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

Teste de rede em dados de teste

Treinamos a rede usando um conjunto de dados de treinamento. Mas precisamos verificar se a rede aprendeu alguma coisa.

Testaremos isso prevendo o rótulo da classe gerado pela rede neural e testando-o para ver se é verdadeiro. Se a previsão estiver correta, adicionamos a amostra à lista de previsões corretas.
Vamos mostrar uma imagem do conjunto 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)))

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

Agora vamos pedir à rede neural que nos diga o que há nessas imagens:


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

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

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

Vamos ver o desempenho da rede 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))

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

Parece que a rede sabe de algo e está funcionando. Se ele determinasse as classes aleatoriamente, a precisão seria de 10%.

Agora vamos ver quais classes a rede identifica 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]))

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

Parece que a rede é melhor na identificação de carros e 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.

Treinando uma rede neural em GPU

Primeiramente, explicarei brevemente o que é CUDA. CUDA (Compute Unified Device Architecture) é uma plataforma de computação paralela desenvolvida pela NVIDIA para computação geral em unidades de processamento gráfico (GPUs). Com CUDA, os desenvolvedores podem acelerar drasticamente os aplicativos de computação aproveitando o poder das GPUs. Esta plataforma já está instalada em nosso servidor que adquirimos.

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 )

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

Enviando a rede para a GPU:

net.to(device)

Também teremos que enviar entradas e alvos em cada etapa para a GPU:

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

Vamos treinar novamente a rede 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 da rede durou cerca de 3 minutos. Lembremos que a mesma etapa em um processador convencional durou 5 minutos. A diferença não é significativa, isso acontece porque a nossa rede não é tão grande. Ao usar grandes arrays para treinamento, a diferença entre a velocidade da GPU e de um processador tradicional aumentará.

Isso parece ser tudo. O que conseguimos fazer:

  • Vimos o que é uma GPU e selecionamos o servidor no qual ela está instalada;
  • Montamos 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 obtivemos um aumento na velocidade.

Terei prazer em responder perguntas nos comentários.

Fonte: habr.com

Adicionar um comentário