Uczenie maszynowe #5 Testujemy sztuczny neuron (implementacja C++)

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@callmejoe·
0.000 HBD
Uczenie maszynowe #5 Testujemy sztuczny neuron (implementacja C++)
<html><div class = text-justify><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"> Ten artykuł będzie podsumowaniem dotychczasowej zdobytej wiedzy teoretycznej z poprzednich części, którą zaprezentujemy sobie w praktyce, używając prostego modelu neuronu liniowego ADALINE zaimplementowanego w języku C++. Artykuł podzielony będzie na dwie sekcje</p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><ul><li><b style="background-color: transparent; font-family: Arial; font-size: 11pt; white-space: pre-wrap;">sekcję ogólną</b>- w której wytłumaczę ideę rozpoznawania przez neuron znaków alfanumerycznych, oraz pokażę jak uruchomić gotowy program bezpośrednio w Twojej przeglądarce.</li><li><b style="background-color: transparent; font-family: Arial; font-size: 11pt; white-space: pre-wrap;">sekcję testów</b> - w której użytkownikowi podda się propozycję modyfikacji wartości poszczególnych zmiennych (współczynnik uczenia, zmiana funkcji aktywacji, dobór bias’u), w celach zaobserwowania zjawisk o których mówiliśmy we wcześniejszych artykułach.</li></ul></p><br><h1 style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Sekcja ogólna</h1><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Otwórz w nowej karcie, edytor kodu online.</p><center><a href="https://www.onlinegdb.com/Bk89s8xOQ">https://www.onlinegdb.com/Bk89s8xOQ&nbsp;&nbsp;</a></center><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Poniżej przedstawiam paint-ściągę jak go używać.</p><center>https://cdn.steemitimages.com/DQmeWJNQBUNzzJmJ5bUsX2PVecPnR8efTdoWNUVGPc2LvVd/rozpiska.png</center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><ol><li><strong>Przycisk “Run”</strong> - wciśnięcie go rozpocznie kompilację kodu, pracę programu oraz prezentację wyników w konsoli</li><li><strong>Konsola</strong> - w niej wyświetlane będą wszystkie wyniki, warto zauważyć że posiada ona uchwyt, którym można zmienić rozmiar konsoli (np. dla wygodniejszej prezentacji wyników)</li><li><strong>Uchwyt do zmiany rozmiaru konsoli</strong> - przytrzymaj i przeciągnij go myszką, by zmienić rozmiar konsoli</li><li><strong>Edytor kodu</strong> - tutaj jest zawarta cała implementacja</li><li><strong>Przycisk “Fork this”</strong>- po jego wciśnięciu możesz edytować kod z 4.</li></ol></p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Po prawej stronie 4. znajduje się suwak - użyj go lub rolki myszki, by zjechać w dół aż do linii nr. 207 (opcjonalnie, by znaleźć ten fragment kodu możesz użyć skrótu klawiszowego “ctrl+f” i w polu frazy którą szukasz wpisać “WEKTORY”)</p><center>https://cdn.steemitimages.com/DQmTnbX9bMgtYEDdzAZ6cRB3nZ9J1idGhoL2wjpcwGsEuYx/wektoryA1.PNG&nbsp;</center><center><br></center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Załóżmy, że do klasyfikacji liter będziemy używać neuronu o stu wejściach, gdzie każde pojedyncze wejście symbolizuje jeden z pikseli obrazka.</p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">W miejscu któremu się przyglądamy zawarte są dane wejściowe w formie wektorów o wymiarach 10x10, które będziemy podawać właśnie na wejście ww. neuronu o 100 dendrytach. Zauważ, że poszczególne cyfry 0 oraz 1 w każdym z wektorów układają się w pewnego rodzaju mozaikę. Umówmy się teraz, że cyfra 1 oznacza piksel litery, a cyfra 0 oznacza piksel tła - w ten prosty sposób możemy dokonać reprezentacji alfanumerycznej każdego symbolu graficznego. </p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Szablony poszczególnych liter wykonałem w sposób trywialny, ręcznie wprowadzając ciąg cyfr do wektora - w praktyce jednak powszechnie stosuje się parsery które formatują grafikę np. fotografię z pismem odręcznym, bezpośrednio do formy cyfrowej czyli właśnie np. takich opakowanych w wektory ciągów liczb jakimi posługujemy się w tym artykule.</p><center>https://cdn.steemitimages.com/DQmT7G1eKNYzvJ2QbWpiz8j5GNgpZqxbRbR5WE6x3vZMPdj/AtoA.png&nbsp;</center><center><br></center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">W tej sekcji nie pozostaje nam nic innego jak wcisnąć przycisk “Run” https://cdn.steemitimages.com/DQmfDSKQzfsju7A2k1h5T4Ye5ERmDFkqsvNm5CkRVpJtLQk/runbutton.PNG i zaobserwować wyniki otrzymane w konsoli.</p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Pierwsza linijka w konsoli oznacza oczywiście liczbę iteracji treningu naszego pojedynczego przypadku litery “A”. Kolejne oznaczają wyniki pojawiające się na wyjściu neuronu dla poszczególnych danych (liter) w procesie pracy. Dla “A” otrzymujemy wynik bliski “1” (prawda), a dla wszystkich innych liter niebędących “A” wynik 0 (fałsz). Możemy więc powiedzieć, że klasyfikacja się powiodła (nie przejmuj się jeśli Twój wynik po przecinku delikatnie różni się od tego na screenie - jest to zupełnie normalne, ze względu na losową inicjację wag początkowych).</p><center>https://cdn.steemitimages.com/DQmfJNs1VbZ57m26KxZqsoBGkKHTi723tQ7Jz7ADvsoEnbQ/console1.PNG&nbsp;</center><br><h1 style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Sekcja testów</h1><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Okej, działa sobie - ale w nawet w tak prostej implementacji powinniśmy mieć przecież pewną kontrolę nad procesem uczenia.</p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Na początku wciśnij przycisk “Fork this” https://cdn.steemitimages.com/DQmRH8GJKeuLHXpeWRei1DhtfmUDndJ2NwPQKDy79hK7nro/forkthis.PNG w górnym lewym rogu okna (obok “Run”) - wciśnięcie ww. przycisku przełączy nas &nbsp;w tryb edycji, innymi słowy od teraz możemy wprowadzać zmiany w naszej implementacji. </p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Posługując się suwakiem zjedź prawie na sam dół aż do linijki 257: (opcjonalnie użyj “ctrl+f” z frazą “TRENING”)</p><center>https://cdn.steemitimages.com/DQmaL4gDFUpUKF9pftPzBqZ7MyiQ97QJBVsXJa9h1Xu39vy/trening.PNG&nbsp;&nbsp;</center><center><br><strong><code>Neuron n1(100, 0.1, “relu”)</code></strong><br>&nbsp;&nbsp;</center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">To oczywiście inicjalizacja neuronu w programie, gdzie wartość 100 oznacza liczbę jego wejść, liczba 0.1 oznacza wartość współczynnika uczenia learningRate, a “relu” wybraną funkcję aktywacji wyjścia neuronu.</p><center>https://cdn.steemitimages.com/DQmdd2n4Cutqi9nbmfm5jUqtttNU37aqnKeVTmrwm6FHB36/trening2.PNG</center><center><br></center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Na potrzebę tej implementacji nie będziemy zmieniać liczby wejść, ponieważ posiadamy próbki liter opakowane w wektory wymiarów 10x10, czyli posiadające 100 komórek - dokładnie tyle co neuron wejść. Z pełną swobodą możemy jednak edytować pozostałe wartości. Na początku zamieńmy fragment mówiący o wyborze funkcji aktywacji. Zastąpmy więc “relu” nazwą “sigmoid” (koniecznie z apostrofami “ “) i ponownie wciśnijmy przycisk “Run” by zaobserwować wyniki.</p><center>https://cdn.steemitimages.com/DQmYjXu6FDYGwiQmqM3EPNciVJcLtgmRLmV5QngMZeuoZv7/trening22.PNG</center><center><br></center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Zauważ, że w tym wypadku wzrosła nam liczba iteracji dla treningu, a jego jakość zdecydowanie spadła - dokładnie o tym mówiliśmy w #4, kiedy było powiedziane że funkcja sigmoidalna zbiega wolniej do żądanego wyniku w porównaniu do reLU. Wniosek jest dosyć prosty - użycie reLU daje lepsze rezultaty :-) Analogiczne obserwacje można wykonać dla funkcji skokowej, przez podanie frazy “step”.</p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Kolejnym elementem, który możemy modyfikować jest współczynnik uczenia learningRate.</p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Tak jak mówiliśmy w #2, delikatnie zwiększając jego wartość możemy przyspieszyć proces treningu. </p><center>https://cdn.steemitimages.com/DQmQE1BfSA7XNi1wDfqKdBrDZZ65jfbFAtKjoGbBEMM9RXr/trening3.PNG</center><center><br></center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Z kolei po przekroczeniu pewnego odpowiednio wysokiego progu, algorytm klasyfikacji zupełnie się rozbiega - a my w efekcie otrzymujemy wyniki dalece odbiegające od oczekiwań.</p><center>https://cdn.steemitimages.com/DQmRF1tckWn5x5BE8L93QH7hsuywGf8kByUDecvAqtJQeDB/trening333.PNG</center><center>https://cdn.steemitimages.com/DQmZ3k3XLvEwc1vgEkXvaH2TMw7o99HNYKakuRedbBz9U3R/trening3333.PNG</center><center><br></center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Ostatnią wartością, którą możemy “regulować” w tej prostej implementacji jest przesunięcie bias. Testy, których dokonywaliśmy przed chwilą dobierały go w sposób “automatyczny”(implementacja posiada prostą funkcję, która się tym zajmuje) - teraz jednak - by pokazać, że ten bias na coś jednak wpływ ma, będziemy go narzucać ręcznie. By włączyć manualną wersję regulacji bias’u przejdźmy do linijki:</p><center><strong><code>// &nbsp;n1.setBias(-50);</code></strong><br>&nbsp;&nbsp;</center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">i usuńmy podwójny backslash “//” na początku tej linii przyłączając ją do kodu (podwójny backslash w wielu językach programowania, również w C++ jest oznaczeniem komentarza który nie jest częścią kodu). Zmieniając wartość znajdującą się między nawiasami, ustawiamy pewną wartość przesunięcia. Na początku ustawmy nasz bias na 0, a pozostałe wartości ustawmy jako learningRate 0.2, aktywacja “relu”.</p><center>https://cdn.steemitimages.com/DQmaX1b4efmYNHsawzh3bFPnnqzgptwGZ1PWxBwGFLAZ5pX/bias0.PNG</center><center><br></center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">W tym momencie powinniśmy otrzymać następujące wyniki:</p><center>https://cdn.steemitimages.com/DQmNoTJ73NugPMfvmMPxRvYU1qAJ3L1wykgX8AtLvWLrsJS/bezbiasu.PNG</center><center><br></center><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">W tym przykładzie można zaobserwować, że bias jednak ma znaczenie - jego brak powoduje, że i owszem litera “A” jest ciągle rozpoznawana jako “prawda”, jednak pozostałe litery których wcale nie chcieliśmy klasyfikować w ten sposób, również są za nią w jakiś sposób uznawane!</p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><br></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Modyfikując wartość biasu (podajemy oczywiście liczbę ujemną, z minusem) możemy otrzymać właściwą klasyfikację, obcinając grupę błędnych wyników (sprawdź dla wartości np. -10 oraz -50).</p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Przeanalizujmy jeszcze kolejne dwie linijki implementacji:</p><center><strong><code>&nbsp;n1.setVectorInput(v_A);&nbsp;</code></strong></center><center><strong><code>&nbsp;n1.tuneWeights(25, 1);</code></strong></center><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Linie te oznaczają kolejno ustawienie wektora wejść dla neuronu, oraz proces treningu dla właśnie tego wektora. Liczba 25 oznacza maksymalną liczbę iteracji treningu, a cyfra 1 - nasze życzenie <code>z</code>, do którego chcemy dostroić synapsy by taki lub zbliżony wynik otrzymywać na wyjściu. Wybierając wektor podawany w ramach treningu, możemy przestroić nasz neuron do rozpoznawania dowolnej innej litery np. “B”.</p><center>https://cdn.steemitimages.com/DQmZwfaX5EjDiyCJfWVkpR9f65d68VgjrKziDEDadbAu8rr/treningB.PNG</center><center><br></center><h2 style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Zakończenie</h2><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">W najbliższym czasie, chciałbym poruszyć temat sekwencyjnego uczenia neuronu. Omawiając to zagadnienie, nie sposób nie powiedzieć o pierwszym napotkanym w tego typu sieciach problemie jakim jest katastroficzna amnezja neuronów.</p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">Warto zaznaczyć, że ta prosta implementacja nie rozwiązuje tego problemu tzn. gdybyśmy chcieli nauczyć nasz pojedynczy neuron rozpoznawania kilku wariantów tej samej litery (np. w &nbsp;różnych czcionkach, lub rozmiarach), co więcej - każdą z propozycji podawalibyśmy seryjnie jedna za drugą - istniałoby ryzyko że neuron byłby zdolny do zapamiętania tylko ostatniego schematu, zapominając sobie o poprzednich. W najbliższym czasie przybliżymy sobie sposoby na rozwiązanie tego niekorzystnego zjawiska, co będzie swoistym wstępem do dynamicznego trenowania sieci.</p><br><strong>Dziękuję za uwagę!</strong></div>
<br>
</html>
👍 , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,