
В тази статия ще разкажа как за 30 минути да настроите среда за машинно обучение, да създадете невронна мрежа за разпознаване на изображения и след това да стартирате същата мрежа на графичен процесор (GPU).
Първо, нека определим какво представлява невронната мрежа.
В нашия случай това е математически модел, а също така и неговото софтуерно или хардуерно проявление, построено по принципа на организацията и функционирането на биологичните невронни мрежи — мрежите на нервните клетки в живия организъм. Това понятие възникна при изучаването на процесите, протичащи в мозъка, и при опитите да се моделират тези процеси.
Невронните мрежи не се програмират в обичайния смисъл на думата, те се обучават. Възможността за обучение е едно от основните предимства на невронните мрежи пред традиционните алгоритми. Технически, обучението се заключава в намирането на коефициентите на свързване между невроните. В процеса на обучение, невронната мрежа е способна да установява сложни зависимости между входни и изходни данни, а също така да изпълнява обобщение.
От гледна точка на машинното обучение, невронната мрежа представлява частен случай на методите за разпознаване на образи, дискриминантния анализ, методите на клъстеризация и други.
Оборудване
Първо, нека се занимаем с оборудването. Необходим ни е сървър с инсталирана операционна система Linux. Оборудването за работа на системи за машинно обучение трябва да е достатъчно мощно и следователно скъпо. На тези, които нямат на разположение добра машина, препоръчвам да разгледат предложенията на облачните доставчици. Необходимият сървър може да бъде нает бързо и да плащате само за времето на ползване.
В проектите, където е необходимо създаването на невронни мрежи, използвам сървъри на един от руските облачни доставчици. Компанията предлага под наем облачни сървъри специално за машинно обучение с мощни графични процесори (GPU) Tesla V100 от NVIDIA. Накратко: използването на сървър с GPU може да бъде десетки пъти по-ефективно (бързо) в сравнение със сървър на същата цена, в който се използва CPU (всички познават централния процесор). Това се постига благодарение на особеностите на архитектурата на GPU, която по-бързо се справя с изчисленията.
За изпълнението на примерите, описани по-долу, ние наехме такъв сървър за няколко дни:
- SSD диск 150 ГБ
- ОЗУ 32 ГБ
- Процесор Tesla V100 16 Gb с 4 ядра
На машината ни е инсталиран Ubuntu 18.04.
Инсталираме среда
Сега ще инсталираме всичко необходимо за работа на сървъра. Тъй като нашата статия е предназначена преди всичко за начинаещи, ще обсъдим някои моменти, които ще бъдат полезни именно за тях.
Много от работата при настройката на средата се извършва чрез командния ред. Повечето потребители използват Windows като работна ОС. Стандартната конзола в тази ОС не е особено удобна. Затова ще използваме удобен инструмент . Сваляме мини версия и стартираме Cmder.exe. След това е необходимо да се свържем с сървъра по протокол SSH:
ssh root@server-ip-or-hostnameВместо server-ip-or-hostname посочвате IP адреса или DNS името на вашия сървър. След това въвеждате паролата и при успешно свързване трябва да получите примерно такова съобщение.
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-74-generic x86_64)Основният език за разработка на ML модели е Python. Най-популярната платформа за неговото използване в Linux е .
Нека я инсталираме на нашия сървър.
Започваме с актуализиране на локалния мениджър на пакети:
sudo apt-get updateИнсталираме curl (командна линия):
sudo apt-get install curlСваляме последната версия на Anaconda Distribution:
cd /tmp
curl –O https://repo.anaconda.com/archive/Anaconda3-2019.10-Linux-x86_64.shСтартираме инсталацията:
bash Anaconda3-2019.10-Linux-x86_64.shПо време на инсталацията ще трябва да потвърдите лицензионното споразумение. При успешна инсталация трябва да видите следното:
Thank you for installing Anaconda3!За разработка на ML модели в момента са създадени множество фреймворкове, с които работим с най-популярните: и .
Използването на фреймворк позволява да се увеличи скоростта на разработка и да се използват готови инструменти за стандартни задачи.
В този пример ще работим с PyTorch. Нека го инсталираме:
conda install pytorch torchvision cudatoolkit=10.1 -c pytorchСега е необходимо да стартираме Jupyter Notebook — популярен инструмент за разработка сред специалистите по ML. Той позволява да пишете код и веднага да виждате резултатите от изпълнението му. Jupyter Notebook е част от Anaconda и вече е инсталиран на нашия сървър. Необходимо е да се свържем с него от нашата работна система.
За това първо ще стартираме Jupyter на сървъра, указвайки порт 8080:
jupyter notebook --no-browser --port=8080 --allow-rootСлед това, отваряйки още един раздел в нашата конзола Cmder (горно меню — New console dialog), ще се свържем по порт 8080 със сървъра чрез SSH:
ssh -L 8080:localhost:8080 root@server-ip-or-hostnameКогато въведем първата команда, ще ни бъдат предложени линкове за отваряне на Jupyter в нашия браузър:
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
Ще използваме линка за localhost:8080. Копирайте целия път и го поставете в адресната лента на локалния браузър на вашия компютър. Jupyter Notebook ще се отвори.
Нека създадем нов ноутбук: New — Notebook — Python 3.
Ще проверим коректността на работата на всички компоненти, които инсталирахме. Въведете в Jupyter примерен код PyTorch и стартирайте изпълнението (бутон Run):
from __future__ import print_function
import torch
x = torch.rand(5, 3)
print(x)
Резултатът трябва да изглежда приблизително така:

Ако получите подобен резултат — значи всичко е настроено правилно и можем да започнем разработката на невронната мрежа!
Създаваме невронна мрежа
Ще създадем невронна мрежа за разпознаване на изображения. За основа ще вземем това .
За обучение на мрежата ще използваме публично достъпен набор от данни CIFAR10. Той има класове: „самолет“, „автомобил“, „птица“, „котка“, „елен“, „куче“, „жаба“, „кон“, „кораб“, „товарен автомобил“. Изображенията в CIFAR10 имат размер 3x32x32, тоест 3-канални цветни изображения с размери 32×32 пиксела.

За работа ще използваме създадения пакет PyTorch за работа с изображения — torchvision.
Следваме следните стъпки по ред:
- Зареждане и нормализация на наборите от обучаващи и тестови данни
- Определяне на невронната мрежа
- Обучение на мрежата на обучаващите данни
- Тестиране на мрежата на тестовите данни
- Ще повторим обучението и тестването, използвайки GPU
Целият посочен код ще изпълняваме в Jupyter Notebook.
Зареждане и нормализация на CIFAR10
Копирайте и изпълнете следния код в Jupyter:
импорт на torch
импорт на torchvision
импорт на torchvision.transforms като 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 = ('самолет', 'кола', 'птица', 'котка',
'сърна', 'куче', 'жаба', 'кон', 'кораб', 'камион')Отговорът трябва да бъде такъв:
Изтегляне на https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz в ./data/cifar-10-python.tar.gz
Разархивиране на ./data/cifar-10-python.tar.gz в ./data
Файловете вече са изтеглени и проверениНека покажем няколко тренировъчни изображения за проверка:
импорт на matplotlib.pyplot като plt
импорт на numpy като np
# функции за показване на изображение
def imshow(img):
img = img / 2 + 0.5 # денормализация
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
# взимаме някои случайни тренировъчни изображения
dataiter = iter(trainloader)
images, labels = dataiter.next()
# показване на изображения
imshow(torchvision.utils.make_grid(images))
# печат на етикети
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

Определяне на невронната мрежа
Нека първо разгледаме как работи невронната мрежа за разпознаване на изображения. Това е проста мрежа с директна свързаност. Тя приема входни данни, пропуска ги през няколко слоя последователно и накрая дава изход.

Нека създадем подобна мрежа в нашата среда:
импорт на torch.nn като nn
импорт на torch.nn.functional като 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()
Нека също така определим функцията за загуби и оптимизатора
импорт на torch.optim като optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)Обучение на мрежата на обучаващите данни
Започваме обучението на нашата невронна мрежа. Обърнете внимание, че след като стартирате този код, трябва да изчакате известно време, докато приключи работата. На мен ми отне 5 минути. За обучението на мрежата е нужно време.
за епоха в диапазона(2): # цикъл през набора от данни многократно
текуща_загуба = 0.0
за i, данни в изброяването(trainloader, 0):
# вземете входовете; данните са списък [входове, етикети]
входове, етикети = данни
# нулиране на параметричните градиенти
оптимизатор.zero_grad()
# напред + назад + оптимизиране
изходи = нет(входове)
загуба = критериал(изходи, етикети)
загуба.backward()
оптимизатор.step()
# отпечатване на статистики
текуща_загуба += загуба.item()
ако i % 2000 == 1999: # отпечатайте на всеки 2000 мини-партиди
print('[%d, ] загуба: %.3f' %
(епоха + 1, i + 1, текуща_загуба / 2000))
текуща_загуба = 0.0
print('Завършено обучение')
Получаваме такъв резултат:

Съхраняваме нашата обучена модел:
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)Тестиране на мрежата на тестовите данни
Обучихме мрежата, използвайки набора от обучителни данни. Но трябва да проверим, дали мрежата изобщо е научила нещо.
Ще проверим това, предсказвайки етикета на класа, който изведе невронната мрежа, и проверявайки го за истинност. Ако прогнозата е вярна, добавяме образеца в списъка с правилни прогнози.
Нека покажем изображение от тестовия набор:
dataiter = iter(testloader)
images, labels = dataiter.next()
# отпечатваме изображения
imshow(torchvision.utils.make_grid(images))
print('Истински резултати: ', ' '.join('%5s' % classes[labels[j]] for j in range(4))) 
Сега нека помолим невронната мрежа да ни каже какво има на тези изображения:
net = Net()
net.load_state_dict(torch.load(PATH))
outputs = net(images)
_, predicted = torch.max(outputs, 1)
print('Предсказано: ', ' '.join('%5s' % classes[predicted[j]]
for j in range(4)))

Резултатите изглеждат доста добри: мрежата е определила правилно три изображения от четири.
Нека видим как мрежата работи върху целия набор от данни.
правилно = 0
total = 0
с torch.no_grad():
за данни в testloader:
изображения, етикети = данни
изходи = net(изображения)
_, предсказано = torch.max(outputs.data, 1)
total += labels.size(0)
правилно += (предсказано == етикети).sum().item()
print('Точността на мрежата върху 10000 тестови изображения: %d %%' % (
100 * правилно / total)) 
Изглежда, мрежата знае нещо и работи. Ако определяше класовете на случаен принцип, точността щеше да е 10%.
Сега да видим кои класове мрежата определя по-добре:
клас_правилен = списък(0. за i в диапазона(10))
клас_общо = списък(0. за i в диапазона(10))
с с torch.no_grad():
за данни в testloader:
изображения, етикети = данни
изходи = нет(изображения)
_, предсказано = torch.max(изходи, 1)
c = (предсказано == етикети).squeeze()
за i в диапазона(4):
етикет = етикети[i]
клас_правилен[етикет] += c[i].item()
клас_общо[етикет] += 1
за i в диапазона(10):
print('Точност на %5s : %%' % (
класове[i], 100 * клас_правилен[i] / клас_общо[i])) 
Изглежда, че мрежата определя най-добре автомобили и кораби: 71% точност.
И така, мрежата работи. Сега нека опитаме да прехвърлим работата ѝ на графичен процесор (GPU) и да видим какво ще се промени.
Обучение на невронна мрежа на GPU
Първо ще обясня накратко какво е CUDA. CUDA (Compute Unified Device Architecture) е платформа за паралелни изчисления, разработена от NVIDIA, за общи изчисления на графични процесори (GPU). С помощта на CUDA разработчиците могат значително да ускорят изчислителните приложения, използвайки възможностите на графичните процесори. На нашия сървър, който сме закупили, тази платформа вече е инсталирана.
Нека първо определим нашия GPU като първото видимо устройство cuda.
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Предполага се, че сме на CUDA машина, това трябва да изведе CUDA устройство:
print(device) 
Изпращаме мрежата на GPU:
net.to(device)Също така ще трябва да изпращаме входовете и целите на всеки шаг и на GPU:
inputs, labels = data[0].to(device), data[1].to(device)Нека стартираме повторно обучението на мрежата вече на GPU:
импорт на torch.optim като оптимизиран
критериал = nn.CrossEntropyLoss()
оптимизатор = оптимизатор.SGD(нет. параметри(), lr=0.001, момент=0.9)
за епоха в диапазона(2): # цикъл през набора от данни многократно
текуща_загуба = 0.0
за i, данни в изброяването(trainloader, 0):
# вземете входовете; данните са списък [входове, етикети]
входове, етикети = данни[0].to(устройство), данни[1].to(устройство)
# нулиране на параметричните градиенти
оптимизатор.zero_grad()
# напред + назад + оптимизиране
изходи = нет(входове)
загуба = критериал(изходи, етикети)
загуба.backward()
оптимизатор.step()
# отпечатване на статистики
текуща_загуба += загуба.item()
ако i % 2000 == 1999: # отпечатайте на всеки 2000 мини-партиди
print('[%d, ] загуба: %.3f' %
(епоха + 1, i + 1, текуща_загуба / 2000))
текуща_загуба = 0.0
print('Завършено обучение')Този път обучението на мрежата продължи около 3 минути. Напомням, че същият етап на обикновен процесор отне 5 минути. Разликата не е значителна, това се дължи на факта, че нашата мрежа не е толкова голяма. При използване на по-големи масиви за обучение, разликата в скоростта между GPU и традиционния процесор ще се увеличава.
На това изглежда, че приключваме. Какво успяхме да направим:
- Разгледахме какво е GPU и избрахме сървър, на който е инсталиран;
- Настроихме програмната среда за създаване на невронна мрежа;
- Създадохме невронна мрежа за разпознаване на изображения и я обучихме;
- Повторихме обучението на мрежата с използване на GPU и получихме увеличаване на скоростта.
С удоволствие ще отговоря на въпросите в коментарите.
Източник: habr.com
