NNI – czyli jak znaleźć optymalne parametry sieci neuronowej?
Poszukiwanie optymalnych parametrów sieci neuronowej jest najczęściej ostatnim etapem przygotowywania modelu sieci neuronowej.
Przemyślana architektura, duży zbiór danych oraz potężne zaplecze obliczeniowe nie gwarantują nam uzyskania oczekiwanej dokładności modelu w dostarczaniu rozwiązań naszego problemu.
Oprócz samego zrozumienia istoty tych parametrów, ważne jest również umiejętne nimi żonglowanie.
Poszukiwanie ich można porównać do obliczania nieco mniej złożonej sieci, dla której szukamy jak najlepszego przyrostu dokładności w naszym bazowym modelu. Do tego celu idealnie nadaje się narzędzie NNI (Neural Network Intelligence).
Czym jest NNI?
NNI to opensource’owy projekt pod banderą Microsoftu, który podczas pisania tego posta był nadal w fazie rozwojowej (v0.5.2).
Mimo to funkcjonalności, jakie oferuje, wystarczają do skutecznego, zautomatyzowanego przeszukiwanie ustalonego zakresu hiperparametrów naszych sieci.
Wydaje się, że na tę chwilę jedyne istotne mankamenty tego narzędzia to brak wsparcia w usuwaniu procesów ze skryptami uczącymi po ręcznym zatrzymaniu eksperymentu lub po wystąpieniu błędu wewnętrznego (#501) oraz brak wsparcia w równomiernym rozdysponowaniu mocy obliczeniowej na kartach graficznych w przypadku, gdy nasz model wykorzystuje jedynie niewielki procent pamięci VRAM (#608 #938).
Nie zapominajmy, że narzędzie to jest nadal w fazie rozwojowej i do rozwiązania większości problemów przyczynić się można samemu.
Największe zalety:
- Obsługa wielu popularnych bibliotek pythonowych dla uczenia maszynowego,
- Możliwość wykorzystania wielu zaimplementowanych już algorytmów tuningu parametrów uczenia NN,
- Możliwość uruchamiania eksperymentów na lokalnej maszynie, rozpraszania ich w chmurze oraz własnej infrastrukturze PAI.
Jak utworzyć eksperyment w NNI?
Na wstępie chciałbym zasugerować, by środowisko dla NNI przygotować, wykorzystując oprogramowanie do wirtualizacji. To właśnie dzięki temu z łatwością będzie można zarządzać zależnościami (jeżeli eksperyment uruchamiamy na fizycznych maszynach). Nie jest to krok niezbędny, aczkolwiek bardzo ułatwiający zarządzanie i obsługę.
Osobiście polecam do tego celu kontenery Dockerowe. W następnym wpisie pokażę, jak utworzyć kontener dla obliczeń z wykorzystaniem GPU.
Wymagania
- System operacyjny Linux (Ubuntu 16.04 lub wyżej), MacOS (10.14.1) (dla użytkowników Windowsa polecam skorzystać z możliwości oferowanych przez kontenery Dockerowe)
- Programy python3 (>= 3.5), pip
- Minimalne wymagania sprzętowe
Instalacja NNI
Po pierwsze, musimy zainstalować sam program.
Ponieważ mamy zainstalowany już wcześniej program pip , wystarczy, że uruchomimy polecenie:
python3 -m pip install --upgrade nni
Konfiguracja eksperymentu
Kolejnym krokiem będzie skonfigurowanie eksperymentu NNI, co w standardowym podejściu sprowadza się do utworzenia dwóch plików:
-
- searchspace.json – plik zawierający zakresy poszukiwań poszczególnych parametrów naszego modelu lub procesu uczenia sieci neuronowej. Oto przykład takiej konfiguracji zaczerpnięty z oficjalnej dokumentacji:
{ "dropout_rate": {"_type": "uniform", "_value": [0.1, 0.5]}, "conv_size": {"_type": "choice", "_value": [2, 3, 5, 7]}, "hidden_size": {"_type": "choice", "_value": [124, 512, 1024]}, "batch_size": {"_type": "choice", "_value": [50, 250, 500]}, "learning_rate": {"_type": "uniform", "_value": [0.0001, 0.1]} }
Kolejne klucze to nazwy zmiennych, do których odwoływać się będziemy w naszym kodzie. W przypisanych im obiektach znaleźć możemy typ poszukiwania – _type oraz zakres – _value . Wybranie typu choice spowoduje losowe wykorzystywanie elementów z listy pod kluczem _value , natomiast wybranie typu uniform da nam możliwość otrzymania losowych wartości z zakresu, którego ramy określamy w polu _value . Pozostałe dostępne typy opisane są w dokumentacji.
- config.yaml – plik konfiguracyjny określający warunki uruchomienia eksperymentu oraz kilka metadanych. Podstawowy szablon ze zmiennymi niezbędnymi do uruchomienia lokalnego eksperymentu wygląda następująco:
authorName: Ermlab.com experimentName: Test experiment trialConcurrency: 2 maxExecDuration: 512h maxTrialNum: 16 #choice: local, remote, pai, kubeflow trainingServicePlatform: local searchSpacePath: searchspace.json #choice: true, false useAnnotation: false tuner: #choice: TPE, Random, Anneal, Evolution builtinTunerName: TPE classArgs: #choice: maximize, minimize optimize_mode: maximize gpuNum: 0 trial: command: python3 -m main codeDir: /home/ermlab/TEST_NNI/ gpuNum: 0
Ponieważ możliwych konfiguracji jest zbyt dużo jak na tak krótki wpis, po szczegółowe wyjaśnienie poszczególnych zmiennych odsyłam tutaj.
- searchspace.json – plik zawierający zakresy poszukiwań poszczególnych parametrów naszego modelu lub procesu uczenia sieci neuronowej. Oto przykład takiej konfiguracji zaczerpnięty z oficjalnej dokumentacji:
Konfiguracja skryptu trenującego
Jako że konfigurację NNI mamy już za sobą, kolejnym krokiem będzie utworzenie bądź też modyfikacja istniejącego skryptu.
Ze względu na zachowanie przejrzystości przykładu załóżmy, że nasz skrypt do uczenia wygląda tak:
import random
from time import sleep
import nni
class Model:
def __init__(self, learning_rate: float, dropout: float, batch_size: int, epochs: int):
self.learning_rate = learning_rate
self.dropout = dropout
self.batch_size = batch_size
self.epochs = epochs
self.metrics = []
def train(self):
for epoch in range(self.epochs):
# Do some real stuff here
print(f'Epoch no {epoch}')
metric: float = random.random()
self.metrics.append(metric)
sleep(1)
nni.report_intermediate_result(metric)
nni.report_final_result(max(self.metrics))
if __name__ == '__main__':
PARAMETERS = nni.get_next_parameter()
print(f'Loaded parameters: {PARAMETERS}')
model = Model(
learning_rate=PARAMETERS.get('learning_rate'),
dropout=PARAMETERS.get('dropout'),
batch_size=PARAMETERS.get('batch_size'),
epochs=PARAMETERS.get('epochs', 100)
)
print('Starting the training')
model.train()
Uruchomienie eksperymentu
Po pierwsze, upewnijmy się, czy w środowisku, w którym będziemy uruchamiać eksperyment, NNI zostało zainstalowane poprawnie:
#nnictl --version 0.5.2
Następnie uruchommy eksperyment poleceniem nnictl create -c config.yaml . Jeżeli wszystko jest skonfigurowane poprawnie, po wejściu na wyświetlony w konsoli adres panelu do zarządzania eksperymentem powinniśmy zobaczyć ekrany podobne do tych, zamieszczonym poniżej:
Podsumowanie
NNI jest bardzo wygodnym oraz intuicyjnym narzędziem do poszukiwania optymalnych parametrów sieci neuronowych. Przejrzystość wyników oraz możliwość wizualizacji najlepszych kombinacji parametrów z pewnością ułatwiają ich analizę. Jednakże największą zaletą NNI jest prawdopodobnie brak potrzeby ręcznego modyfikowania ustawień treningu po każdym zakończonym treningu, co w rezultacie umożliwia prawie pełną automatyzację wytrenowania modelu wynikowego.
Podsumowując, porównywanie wyników treningu, wczesne zatrzymywanie źle rokujących sesji treningowych oraz algorytmy konsekwentnie optymalizujące dobór parametrów to najistotniejsze, a przede wszystkim bardzo przydatne cechy tego oprogramowania.