POL
ENG

Prosta sieć neuronowa

Skrót AI oznaczający sztuczną inteligencje pojawia się co raz częściej w różnych dziedzinach naszego życia. Postanowiłem zgłębić temat i przyjrzeć mu się od kuchni. Podczas jednego z przyjęć urodzinowych, miałem okazję rozmawiać z Robertem O., który polecił mi bardzo ciekawy kanał na YT o tej tematyce (Andrew Ng - @ArtificialIntelligenceAllinOne). Autor kanału w ciekawy i przystępny sposób mówił o podstawach tej technologii. To zachęciło mnie do dalszego szukania i chwilę później trafiłem na inny gdzie w graficzny sposób był wytłumaczony przepływ informacji w takim modelu (@3blue1brown - https://www.youtube.com/@3blue1brown). To pozwoliło mi zrozumieć podstawy.

Pewnego dnia wracając do domu, musiałem kolejny raz szukać wolnego miejsca parkingowego pod swoim domem. Wtedy w głowie zakiełkował mi pomysł na aplikację, która wskazywałaby wolne miejsca zanim dojadę do parkingu. Od pomysłu do realizacji była długa droga. Na początek musiałem zaznajomić się z metodami programowego tworzenia sieci neuronowych.

Narzędzia

Implementacja podstawowego neuronu jest trywialna. Połączenie ich w sieć też nie jest trudne. Trochę więcej pracy wymaga zaimplementowanie wstecznej propagacji i uczynienie kodu uniwersalnym i łatwo skalowalnym. Tylko po co wyważać otwarte drzwi. W internecie można spotkać kilka gotowych rozwiązań, tj. OpenNN, PyTorch lub TensorFlow. Ja zdecydowałem się na ten ostatni.

Implementacja

Najczęściej, swoją przygodę z sieciami neuronowymi, ludzie rozpoczynają od przykładu z rozpoznawaniem pisma ręcznego. Prawie na każdej stronie lub filmie można to znaleźć. Mimo, że powszechny, to nie jest to najprostszy przykład. Potrzebowałem przykładu na tyle prostego na pierwszy raz, żeby w razie trudności umieć go prześledzić. I tak oto powstała moja najprostsza sieć neuronowa.

Sieć

Zadanie, które ma rozwiązać, to znalezienie na czarno-białym obrazku czarnej, prostej kreski o dowolnej grubości, orientacji i nachyleniu. Obraz ma oszałamiające wymiary 3x3 i składa się z pikseli czarnych i białych, bez odcieni szarości.

Struktura

Każdy obrazek jest przekształcany to wektora zawierającego wszystkie 9 bitów. Dlatego warstwa wejściowa ma 9 elementów. Sygnały z niej trafiają do dwóch szeregowych warstw ukrytych. Wyjście składa się z dwóch elementów, chociaż ostatecznie mogłoby zawierać tylko jeden. Sygnał na wyjściu jest dwustanowy i oznacz, że rysunek wejściowy reprezentuje linię lub nie. Przykład jest na tyle prosty, że można by go rozwiązać układem kombinacyjnym, ale nie o to tu chodzi :).

model = keras.Sequential(name="FirstNN")

model.add(layers.Dense(

input_shape=(9,),

units=l1_units,

activation="relu",

use_bias = True,

name="Hidden1"))

model.add(layers.Dense(

units=l2_units,

activation="relu",

use_bias = True,

name="Hidden2"))

model.add(layers.Dense(

units=2,

activation="softmax",

use_bias = True,

name="Output"))

Uczenie

Jak już pisałem, zdecydowałem się na użycie TensorFlow w wydaniu Pythonowym. Oprogramowanie to zawiera zintegrowany pakiet Keras, który umożliwia wygodną, wysokopoziomową zabawę z sieciami neuronowymi. Model sieci, to sekwencyjna struktura składającą się z wyżej wymienionych warstw. Po utworzeniu modelu, sieć nie spełnia swojej roli, jeżeli nie przejdzie procesu uczenia. Do tego celu stosuje się zestaw danych wejściowych w parze ze zdefiniowanymi wynikami jej działania np.: zestaw obrazków, na których wiemy co jest przedstawione. Takie dane przepuszczamy przez sieć wskazując jej na ile wynik jej działania był daleki od oczekiwanego. Znając tę różnicę, algorytm uczenia wprowadza korektę czułości poszczególnych neuronów.

W przypadku tego modelu, wejście jest 9-cio bitowe, zatem ilość kombinacji czarno-białych obrazów wynosi 512 sztuk. Oczywiście można by użyć całej tej kolekcji, ale może wtedy wystąpić efekt przekarmienia (overfeeding) sieci, polegającym na tym, że potrafi dobrze rozpoznać tylko znane jej wzorce, a z nowymi sobie nie radzi. Dlatego stosuje się wydzielenie mniejszej części całej kolekcji jako próbek do uczenia, drugiej takiej części do weryfikacji, a reszta jak dane nieznane przeznaczone do normalnego działania. I tak też było w tym przypadku.

# Prepare random indexes for training samples

train_indexes = get_unique_vector(0, len(input_data), training_size)

# Prepare random indexes for validation samples

val_indexes = get_unique_vector(0, len(input_data), val_size)

Pakiet Keras pozwala w prosty sposób zainicjować proces uczenia wywołując pojedynczą komendę. Można przekazać jej nieco więcej parametrów zwiększając jej możliwości:

model.fit(

x_train,

y_train,

epochs = epochs,

callbacks = callbacks,

validation_data=(x_val, y_val),

use_multiprocessing=True,

workers=4,

verbose=0,

)

Po nauczeniu naszej sieci rozpoznawania wybranego przez nas wzorca warto zapisać model, by nie musieć powtarzać tego procesu za każdym razem. Pamiętajmy, że to trywialny przykład, który trwa kilka sekund. Dużo bardziej skomplikowane architektury operujące na potężnych kolekcjach danych, szkolone na bardzo szybkich komputerach potrafią prowadzić trening liczony w godzinach i dniach. Takiej pracy lepiej nie odtwarzać przy każdym uruchomieniu. Do zapisania gotowego modelu służy wbudowane polecenie:

model.save()

I tak powstała ta sieć. Jak już wspomniałem, pełna kolekcja składa się z 512 obrazków. Można ją zobaczyć poniżej.

Pelna kolekcja

Nie zawsze radzi sobie idealnie. Po kilku iteracjach udało mi się osiągnąć dokładność na poziomie 92,16%. Da się więcej. Te brakujące kilka procent skutkuje błędną interpretacją niektórych obrazów. Tutaj przykład tych uznanych za linię:

Zle rozpoznanie

Mimo to, jak na pierwszy projekt tego typu jestem zadowolony. Na szczęście nie jest to część autonomicznego drona albo samochodu ;).

Wszelkie prawa zastrzeżone. Projekt i wykonanie strony SrcPro.pl