V tem članku vam bom povedal, kako v 30 minutah vzpostaviti okolje strojnega učenja, ustvariti nevronsko mrežo za prepoznavanje slik in nato isto mrežo zagnati na grafičnem procesorju (GPE).
Najprej opredelimo, kaj je nevronska mreža.
V našem primeru je to matematični model, pa tudi njegova programska ali strojna izvedba, zgrajena na principu organizacije in delovanja bioloških nevronskih mrež - mrež živčnih celic živega organizma. Ta koncept je nastal med preučevanjem procesov, ki se dogajajo v možganih, in poskusom modeliranja teh procesov.
Nevronske mreže niso programirane v običajnem pomenu besede, ampak so usposobljene. Sposobnost učenja je ena glavnih prednosti nevronskih mrež pred tradicionalnimi algoritmi. Tehnično je učenje sestavljeno iz iskanja koeficientov povezav med nevroni. V procesu usposabljanja je nevronska mreža sposobna prepoznati kompleksne odvisnosti med vhodnimi in izhodnimi podatki ter izvesti posplošitev.
Z vidika strojnega učenja je nevronska mreža poseben primer metod za prepoznavanje vzorcev, diskriminantne analize, metod združevanja v gruče in drugih metod.
Оборудование
Najprej si oglejmo opremo. Potrebujemo strežnik z nameščenim operacijskim sistemom Linux. Oprema, ki je potrebna za delovanje sistemov strojnega učenja, je precej zmogljiva in posledično draga. Tistim, ki pri roki nimate dobrega stroja, priporočam, da ste pozorni na ponudbe ponudnikov v oblaku. Zahtevani strežnik lahko hitro najamete in plačate le čas uporabe.
Pri projektih, kjer je potrebno ustvariti nevronske mreže, uporabljam strežnike enega od ruskih ponudnikov oblakov. Podjetje ponuja najem strežnikov v oblaku posebej za strojno učenje z zmogljivimi grafičnimi procesorji Tesla V100 (GPU) podjetja NVIDIA. Na kratko: uporaba strežnika z GPE je lahko desetkrat učinkovitejša (hitrejša) v primerjavi s strežnikom podobne cene, ki za izračune uporablja CPE (dobro znana centralna procesna enota). To je doseženo zaradi značilnosti arhitekture GPU, ki se hitreje spopada z izračuni.
Za izvedbo spodaj opisanih primerov smo za več dni kupili naslednji strežnik:
- SSD disk 150 GB
- RAM 32 GB
- Tesla V100 16 Gb procesor s 4 jedri
Na naš računalnik smo namestili Ubuntu 18.04.
Postavitev okolja
Zdaj pa namestimo vse, kar je potrebno za delo na strežniku. Ker je naš članek namenjen predvsem začetnikom, bom govoril o nekaterih točkah, ki jim bodo koristne.
Veliko dela pri nastavljanju okolja se opravi prek ukazne vrstice. Večina uporabnikov uporablja Windows kot delovni OS. Standardna konzola v tem operacijskem sistemu pušča veliko želenega. Zato bomo uporabili priročno orodje
ssh root@server-ip-or-hostname
Namesto server-ip-ali-hostname določite naslov IP ali ime DNS vašega strežnika. Nato vnesite geslo in če je povezava uspešna, bi morali prejeti podobno sporočilo.
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-74-generic x86_64)
Glavni jezik za razvoj modelov ML je Python. In najbolj priljubljena platforma za njegovo uporabo v Linuxu je
Namestimo ga na naš strežnik.
Začnemo s posodobitvijo lokalnega upravitelja paketov:
sudo apt-get update
Namestite curl (pripomoček ukazne vrstice):
sudo apt-get install curl
Prenesite najnovejšo različico Anaconda Distribution:
cd /tmp
curl –O https://repo.anaconda.com/archive/Anaconda3-2019.10-Linux-x86_64.sh
Začnimo namestitev:
bash Anaconda3-2019.10-Linux-x86_64.sh
Med postopkom namestitve boste morali potrditi licenčno pogodbo. Po uspešni namestitvi bi morali videti tole:
Thank you for installing Anaconda3!
Za razvoj modelov ML je bilo ustvarjenih veliko ogrodij; delamo z najbolj priljubljenimi:
Uporaba ogrodja vam omogoča, da povečate hitrost razvoja in uporabite že pripravljena orodja za standardne naloge.
V tem primeru bomo delali s PyTorch. Namestimo ga:
conda install pytorch torchvision cudatoolkit=10.1 -c pytorch
Zdaj moramo zagnati Jupyter Notebook, priljubljeno razvojno orodje za strokovnjake za ML. Omogoča pisanje kode in takojšen ogled rezultatov njene izvedbe. Jupyter Notebook je vključen v Anacondo in je že nameščen na našem strežniku. Nanj se morate povezati iz našega namiznega sistema.
Da bi to naredili, bomo najprej zagnali Jupyter na strežniku z navedbo vrat 8080:
jupyter notebook --no-browser --port=8080 --allow-root
Nato odpremo drug zavihek v naši konzoli Cmder (zgornji meni - pogovorno okno Nova konzola) in se prek vrat 8080 povežemo s strežnikom prek SSH:
ssh -L 8080:localhost:8080 root@server-ip-or-hostname
Ko vnesemo prvi ukaz, se nam v brskalniku ponudijo povezave za odpiranje Jupyterja:
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
Uporabimo povezavo za localhost:8080. Kopirajte celotno pot in jo prilepite v naslovno vrstico lokalnega brskalnika vašega računalnika. Odpre se Jupyter Notebook.
Ustvarimo nov zvezek: Novo – Beležnica – Python 3.
Preverimo pravilno delovanje vseh komponent, ki smo jih namestili. V Jupyter vnesemo primer kode PyTorch in zaženemo izvedbo (gumb Zaženi):
from __future__ import print_function
import torch
x = torch.rand(5, 3)
print(x)
Rezultat bi moral biti nekaj takega:
Če imate podoben rezultat, potem smo vse pravilno konfigurirali in lahko začnemo razvijati nevronsko mrežo!
Ustvarjanje nevronske mreže
Ustvarili bomo nevronsko mrežo za prepoznavanje slik. Vzemimo to za osnovo
Za usposabljanje omrežja bomo uporabili javno dostopen nabor podatkov CIFAR10. Ima razrede: "letalo", "avto", "ptica", "mačka", "jelen", "pes", "žaba", "konj", "ladja", "tovornjak". Slike v CIFAR10 so 3x32x32, torej 3-kanalne barvne slike 32x32 slikovnih pik.
Za delo bomo uporabljali paket, ki ga je ustvaril PyTorch za delo s slikami - torchvision.
Naslednje korake bomo izvedli po vrstnem redu:
- Nalaganje in normalizacija nizov podatkov o usposabljanju in testih
- Definicija nevronske mreže
- Omrežno usposabljanje na podatkih o usposabljanju
- Testiranje omrežja na testnih podatkih
- Ponovimo usposabljanje in testiranje z GPU
Vso spodnjo kodo bomo izvedli v Jupyter Notebook.
Nalaganje in normalizacija CIFAR10
Kopirajte in zaženite naslednjo kodo v Jupyterju:
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')
Odgovor bi moral biti:
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
Prikažimo več slik za vadbo za testiranje:
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)))
Definicija nevronske mreže
Najprej razmislimo, kako deluje nevronska mreža za prepoznavanje slik. To je preprosto omrežje od točke do točke. Vzame vhodne podatke, jih prenese skozi več plasti enega za drugim in nato končno proizvede izhodne podatke.
Ustvarimo podobno mrežo v našem okolju:
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()
Definiramo tudi funkcijo izgube in optimizator
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
Omrežno usposabljanje na podatkih o usposabljanju
Začnimo trenirati našo nevronsko mrežo. Upoštevajte, da boste morali po zagonu te kode počakati nekaj časa, da bo delo končano. Vzelo mi je 5 minut. Za usposabljanje omrežja je potreben čas.
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')
Dobimo naslednji rezultat:
Shranimo naš usposobljeni model:
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
Testiranje omrežja na testnih podatkih
Omrežje smo usposobili z naborom učnih podatkov. Moramo pa preveriti, ali se je omrežje sploh česa naučilo.
To bomo preizkusili tako, da bomo predvideli oznako razreda, ki jo izpiše nevronska mreža, in jo testirali, da vidimo, ali je resnična. Če je napoved pravilna, dodamo vzorec na seznam pravilnih napovedi.
Pokažimo sliko iz testnega niza:
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)))
Zdaj pa prosimo nevronsko mrežo, da nam pove, kaj je na teh slikah:
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)))
Rezultati se zdijo precej dobri: omrežje je pravilno identificiralo tri od štirih slik.
Poglejmo, kako deluje omrežje v celotnem naboru podatkov.
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))
Videti je, da omrežje nekaj ve in deluje. Če bi razrede določil naključno, bi bila natančnost 10 %.
Zdaj pa poglejmo, katere razrede omrežje bolje identificira:
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]))
Zdi se, da je omrežje najboljše pri prepoznavanju avtomobilov in ladij: 71-odstotna natančnost.
Omrežje torej deluje. Zdaj pa poskusimo prenesti njegovo delo na grafični procesor (GPU) in poglejmo, kaj se bo spremenilo.
Usposabljanje nevronske mreže na GPU
Najprej bom na kratko razložil, kaj je CUDA. CUDA (Compute Unified Device Architecture) je vzporedna računalniška platforma, ki jo je razvila NVIDIA za splošno računalništvo na grafičnih procesnih enotah (GPU). S CUDA lahko razvijalci dramatično pospešijo računalniške aplikacije z izkoriščanjem moči grafičnih procesorjev. Ta platforma je že nameščena na našem strežniku, ki smo ga kupili.
Najprej definirajmo naš GPE kot prvo vidno napravo cuda.
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 )
Pošiljanje omrežja v GPE:
net.to(device)
GPE-ju bomo morali na vsakem koraku poslati tudi vnose in cilje:
inputs, labels = data[0].to(device), data[1].to(device)
Ponovno usposobimo omrežje 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')
Tokratni mrežni trening je trajal približno 3 minute. Spomnimo se, da je ista stopnja na običajnem procesorju trajala 5 minut. Razlika ni bistvena, to se zgodi, ker naše omrežje ni tako veliko. Pri uporabi velikih nizov za usposabljanje se bo razlika med hitrostjo GPE in tradicionalnim procesorjem povečala.
Zdi se, da je to vse. Kaj nam je uspelo:
- Pogledali smo, kaj je GPE in izbrali strežnik, na katerem je nameščen;
- Vzpostavili smo programsko okolje za ustvarjanje nevronske mreže;
- Ustvarili smo nevronsko mrežo za prepoznavanje slik in jo usposobili;
- Ponovili smo omrežno usposabljanje z GPE in prejeli povečanje hitrosti.
Z veseljem bom odgovoril na vprašanja v komentarjih.
Vir: www.habr.com