DLJ4: Jak wykonać sieć neuronową tworzącą obrazy – Poradnik krok po kroku

DLJ4: Jak wykonać sieć neuronową tworzącą obrazy – Poradnik krok po kroku

Sieci neuronowe, uczenie maszynowe, czy sztuczna inteligencja – można odnieść wrażenie, że ostatnio jesteśmy tymi hasłami atakowani niemal z każdej strony. Są one kojarzone przede wszystkim z potentatami z branży IT, którzy co rusz informują o spektakularnych postępach na tym polu. Postanowiłem nieco odczarować mity i wyobrażenia o uczeniu maszynowym poprzez serię artykułów wyjaśniających to zagadnienie, podpierając się przy tym efektownymi przykładami. W tym artykule zaprezentuję w jaki sposób krok po kroku zbudować i nauczyć sieć neuronową generowania jednego obrazu na bazie innego.

Agenda
  1. Czego dowiesz się z tego poradnika
  2. Przygotowanie aplikacji wraz z GUI
  3. Modelowanie sieci neuronowej
  4. Przygotowanie danych wejściowych
  5. Uczenie sieci neuronowej
  6. Test aplikacji

 

Źródła aplikacji

Kody źródłowe aplikacji opisanej w tym poradniku znajdziesz w katalogu 01-neural-network-painter znajdującym się w repozytorium:

 

Dodatkowe zasoby:

 

1. Czego dowiesz się z tego poradnika

W tym poradniku stworzymy sieć neuronową, przed którą postawimy zadanie przekształcenia jednego obrazu w drugi. Aplikacja zostanie zbudowana na bazie opisanego we wcześniejszym artykule szablonu aplikacji DL4J opartej o JavaFx oraz Spring Boot. Dzięki temu, że skorzystamy niego, to ilość kodu niezbędnego do zaimplementowania została zredukowana zaledwie do jednego kontrolera i jednej formatki.

Mimo niewielkiej ilości kodu uda nam się przebrnąć przez najważniejsze zagadnienia uczenia maszynowego. Podróż rozpoczniemy od przygotowania i przeskalowania danych treningowych i testowych. W dalszej części utworzymy model sieci neuronowej adekwatny do naszych potrzeb, który następnie nauczymy rozwiązywać postawione przed nim zadanie. Na koniec odpytamy sieć o wynik, który zwizualizujemy w postaci obrazu.

Zastosowany szablon aplikacji umożliwi nam śledzenie postępów uczenia, oraz wizualizację aktualnego wyniku sieci w trakcie procesu uczenia. Poniżej znajduje się zrzut ekranu z aplikacji po wykonaniu blisko 100,000 iteracji procesu uczenia.

 

Jeżeli nie zapoznałeś się jeszcze z szablonem aplikacji, to teraz jest odpowiedni moment aby to zrobić:

 

2. Przygotowanie aplikacji wraz z GUI

Naszą aplikację zbudujemy na wspomnianym wcześniej szablonie, w związku z tym pobierz go z repozytorium, rozpakuj a następnie zaimportuj projekt poprzez otwarcie pliku build.gradle w preferowanym środowisku programistycznym. Osobiście polecam IntelliJ IDEA. Nazwę projektu możesz zmienić edytując plik settings.gradle.

 

 

Kolejnym krokiem jest dorobienie brakującej części GUI. W przypadku tego artykułu będzie to formatka składająca się z trzech obrazków. Pierwszy, to obrazek, który sieć neuronowa otrzymuje na wejściu. Drugi, jest odpowiedzią sieci(rezultatem) przekształconym na obraz. Trzeci natomiast, to obraz do którego dążymy.

Zanim jednak zaimplementujemy formatkę, przygotuj lub pobierz trzy obrazy o wymiarach 100×100 px i umieść je w katalogu src/main/resources/data. Nazwij je input.png, expected.png, oraz test.png. Obraz test.png posłuży nam do sprawdzenia reakcji sieci neuronowej na inny obraz na wejściu niż ten wyuczony. Obrazy możesz pobrać z repozytorium.

 

GUI implementujemy poprzez edycję pliku fxml/custom-learning-gui.fxml. Moja implementacja wygląda jak na listingu poniżej. Ponieważ grafika o wymiarach 100×100 jest dość mała na ekranie Full HD komputera, to postanowiłem ją powiększyć trzykrotnie. Warto w tym miejscu zwrócić uwagę na kilka elementów:

  • Formatka ma zdefiniowany kontroler, który pochodzi z szablonu: fx:controller="com.bettercoding.dl4j.controller.CustomLearningGuiControllerImpl"
  • Kontrolki, których będziemy chcieli użyć w kontrolerze są opisane atrybutem fx:id np. fx:id="inputImageView".

 

Następnie pora na zainicjowanie w kontrolerze zdefiniowanych obrazów poprzez edycję klasy CustomLearningGuiControllerImpl:

 

Uruchomienie aplikacji com.bettercoding.dl4j.Dl4JApp na tym etapie powinno dać rezultat jak poniżej:

 

3. Modelowanie sieci neuronowej

Nadszedł moment, w którym przystępujemy do zaprojektowania i implementacji pierwszej sieci neuronowej. Nim jednak to zrobimy niezbędne będzie zdobycie podstaw o jej budowie.

Sieci neuronowe początkowo wzorowane były na działaniu komórek nerwowych i moim zadaniem nadal jest to dość dobra analogia do wyobrażenia sobie zasady działania oraz procesów, które w nich zachodzą.  Podstawowym elementem sieci neuronowej jest neuron. W najprostszej postaci składa się on z wejść, wag, sumatora, funkcji aktywacji oraz wyjścia. Wejścia oraz ich wagi są odpowiednikiem dendrytów w komórce nerwowej. Sumator i funkcja aktywacji pełnią rolę analogiczną do jądra komórkowego, natomiast wyjście neuronu to odpowiednik aksonów.

 

Sygnały płynące z poszczególnych wejść są mnożone przez wagę odpowiadającą temu wejściu i sumowane ze sobą. Następnie liczony jest wynik funkcji aktywacji dla wspomnianej sumy. Funkcją taką może być np. funkcja liniowa lub sigmoidalna.

 

Uczenie neuronu polega na uwrażliwieniu go na sygnał pochodzący z któregoś z wejść poprzez modyfikację wagi tego wejścia, co wpływa na udział tego wejścia w sumie. Np. Jeśli zwiększymy wartość wagi pierwszego wejścia dziesięciokrotnie, to udział tego sygnału w sumie również zwiększy, co z kolei spowoduje osłabienie reakcji na pozostałe wejścia.

Jeśli neurony ułożymy w warstwy tak, że połączymy wyjścia neuronów z wejściami neuronów kolejnej warstwy, to otrzymamy sieć neuronową. Poniżej zaprezentowana jest trójwarstwowa sieć neuronowa z trzema neuronami wejściowym, dwoma w warstwie ukrytej, oraz jedynym w warstwie wyjściowej.

 

Na potrzeby tego artykułu zaimplementujemy sieć neuronową stworzoną z czterech warstw ukrytych oraz jednej wyjściowej. Użyjemy dwóch różnych funkcji aktywacji, a mianowicie Activation.IDENTITY(liniowej) oraz Activation.LEAKYRELU. Sieć posiada 5 neuronów wejściowych będących odpowiednio współrzędnymi x i y pixela obrazu wejściowego, oraz jego kolorem rozbitym na składowe czerwoną, zieloną i niebieską. Warstwa wyjściowa posiada 3 neurony będące kolorem pixela wyjściowego, który odpowiada pozycji pixela wejściowego.

 

Opisaną sieć neuronową implementujemy dodając do klasy pliku CustomLearningGuiControllerImpl poniższy kod:

 

4. Przygotowanie danych wejściowych

Pora na przygotowanie przykładów, na bazie których nauczymy naszą sieć postawionego przed nią zadania. W przypadku tego artykułu będzie to kolekcja wektorów (jeden na każdy pixel, czyli w sumie 10 000) składających się z 5 cech (x,y,r,g,b) oraz spodziewanego wyniku – 3 etykiet (r,g,b). DL4j dostarcza struktury danych, która służy właśnie do tego celu, a mianowicie chodzi o DataSet.

 

Konwersji obrazu na DataSet dokonujemy funkcją ImageUtils.convertToDataSet jak poniżej. Nie będę tu omawiał w jaki sposób dokonujemy tej konwersji, ponieważ kod zawarty w repozytorium jest wystarczająco jasny.

 

Na tym etapie przygotujemy również dane na potrzeby testu, który będziemy wykonywać co pięć sekund. Będzie on polegał na przeliczeniu wyniku sieci dla wszystkich punktów obrazka wejściowego, a następnie złożenie obrazka wynikowego z serii odpowiedzi sieci neuronowej. W związku z tym będziemy potrzebowali zmiennej:

 

5. Uczenie sieci neuronowej

Przez sam proces uczenia przejdę pobieżnie, ponieważ jego szczegółowe omówienie wymaga zgłębienia wiedzy z tematów, których jeszcze nie poruszaliśmy. Na tym etapie wystarczy CI wiedza, ze korzystamy z algorytmu wstecznej propagacji błędów. Sama implementacja procesu uczenia sieci polega na wybraniu próbki 1000 punktów, a następnie uruchomienie pojedynczego przebiegu uczenia wywołując neuralNetwork.fit, tak jak zostało to przedstawione poniżej:

 

Dodatkowo przygotowany przez mnie framerowk odświeża GUI co 5s wywołując przy tym metodę onRefreshGUI. Możemy ją wykorzystać do przeliczenia przez sieć wszystkich punktów obrazka wyjściowego i jego wyświetlenie na GUI. Dzięki temu na bieżąco możemy obserwować postęp uczenia.

 

Ostatnią metodą do zaimplementowania pozostało obsłużenie przycisku Test. Poniższy kod, to implementacja polegająca na wczytaniu pliku test.png, następnie przetworzeniu go przez sieć neuronową oraz wyświetleniu w kontrolce outputImage.

 

6. Test aplikacji

Gotową aplikację możesz pobrać z repozytorium. Poniżej przedstawiłem kilka zrzutów ekranu pokazujących jak wyglądał proces uczenia się sieci w zależności od ilości treningów.

 

To byłoby na tyle jeśli chodzi o ten wpis. Jeśli podoba Ci się konwencja i chciałbyś więcej takich wpisów, zostaw mi komentarz z tematem, który Cię interesuje a ja postaram się go przygotować. Tymczasem możesz mi pomóc dotrzeć do większej liczby odbiorców udostępniając ten wpis.Dziękuję

 

Leave a Reply

avatar
  Subscribe  
Notify of
Close Menu