Mój wstępniak na You Tube
W moim ostatnim wpisie (patrz Pardon my French, but the thing is really intelligent ) opisałem moją pierwszą przygodę z programowaniem sieci neuronalnej. Wyszukałem w Internecie jeden z najprostszych możliwych algorytmów w języku Python (znalazłem go w artykule How to build your own Neural Network from scratch in Python ) i zastosowałem do niewielkiej próbki danych na temat efektywności energetycznej w skali makroekonomicznej. Sieciami neuronalnymi zajmuję się z dwóch powodów. Po pierwsze, w mojej branży, czyli w naukach społecznych, estymacja modeli ekonometrycznych przy pomocy regresji liniowej testowanej metodą najmniejszych kwadratów zaczyna już trącić myszką. Modele oparte na sztucznej inteligencji albo przynajmniej testowane przy pomocy sieci neuronalnych zaczynają wypierać to, co można już dzisiaj określić jako tradycyjną statystykę probabilistyczną. Redaktor naczelny czasopisma „Energy Economics” właśnie zwrócił mi manuskrypt artykułu, z grzeczną lecz stanowczą sugestią, że w dziedzinie badań empirycznych nad efektywnością energetyczną warto wreszcie wyjść z jaskini i zacząć stosować nowsze metody badawcze. Święta prawda, Herr Redaktor, staram się nadgonić.
Po drugie, mam taką małą, prywatną i jednocześnie uogólnioną obsesję badawczą: staram się badać działanie naszych ludzkich struktur społecznych jako przejawu zbiorowej inteligencji. No dobra, może nie zawsze jesteśmy szczególnie bystrzy jako zbiorowość, ale jednak jakoś dajemy radę. No weźmy na przykład wynalezienie koła. Gdyby dzisiaj ktoś się pojawił, z tryumfalną miną, dzierżąc świeżo wynalezione koło, zaraz spotkałby się z krytyką: „Jedno koło ? A co Ty chcesz zrobić z jednym kołem ? Maszynę losującą ? Przecież trzeba co najmniej dwóch kół, no i jakiejś ośki. Z jednym kołem to nawet hulajnogi nie wynajdziemy”. W sumie racja, ale patrzcie: kiedyś ktoś wynalazł jedno koło i potem wszyscy pozostali mieli wystarczająco dużo cierpliwości, żeby wynaleźć drugie, potem oś łączącą oba, jeszcze później wynaleźli woły do ciągnięcia tego czegoś na tych dwóch kołach. To wszystko wymagało dalekowzrocznej i cierpliwej pracy, czyli przejawu zbiorowej inteligencji.
Wracam do opisu mojego pierwszego doświadczenia z algorytmem sieci neuronalnej. Wiem, że dla czytającego ten wpis informatyka moje zachwyty neofity mogą być żałosne, jednak cóż: to jest mój dziennik naukowy. Opisuję w nim uczciwie moją intelektualną podróż, a w każdej podróży po prostu trzeba przejść przez kolejne punkty i już. Moje podejście do tego konkretnego algorytmu było nieco odmienne od tego, jakie pokazał autor cytowanego przeze mnie artykułu. On sprawdził, ile iteracji potrzeba tej sieci neuronalnej dla zminimalizowania błędu w estymacji wartości wyjściowych z prostego zbioru wartości wejściowych. Interesowała go skuteczność. Ja z mojej strony obserwuję przede wszystkim sposób działania sieci neuronowej i jej strukturę tak, żebym mógł te obserwacje uogólnić na działanie czegokolwiek inteligentnego. Ja wziąłem więc ten prościutki algorytm sieci neuronalnej, przełożyłem go na powtarzalną sekwencję obliczeń i ręcznie wykonywałem kolejne rundy tychże obliczeń w Excelu, na niewielkiej próbce moich danych empirycznych na temat efektywności energetycznej w skali makro. Głupie ? Po co wykonywać obliczenia ręcznie, kiedy w algorytmie wpisana jest pętla i jej zadaniem jest automatyczne powtarzanie obliczeń ? No właśnie po to, żeby zrozumieć dokładnie jak to działa, krok po kroku.
Działa to w sposób, od którego szczęka mi opadła. Układ czterech (no, sześciu) równań, wykonywanych w zapętlonej sekwencji, najpierw generuje błędy w predykcji wartości zmiennej wyjściowej, potem zmniejsza skalę tych błędów, a następnie znowu ją zwiększa po to, aby potem znowu ją zmniejszyć. To coś – w sensie ten układ równań – generował błędy, na podstawie których następnie zmniejszał błędy, ale potem znowu generował kolejne błędy, jakby eksperymentował z dostępnym zbiorem danych. Układ równań, który mógłby być na rozszerzonej maturze z matematyki zachowywał się jak inteligentny organizm !
Kiedy patrzyłem na skumulowany błąd predykcji, jaki tworzył ten układ równań, przyszedł mi do głowy obraz kotów moich sąsiadów, kiedy łażą po moim ogródku (koty, nie sąsiedzi). W ciepłe i słoneczne dni (czyli raczej nie teraz), dach mojej szopy na narzędzia to taka plaża dla VIP-ów. Okoliczne koty uwielbiają się tam wygrzewać, ale uwaga: mimo że dach jest spory (jak dla kota), w sensie moralnym jest tam miejsce tylko dla jednego. Każdy kot, który wtedy wejdzie do mojego ogrodu, rozgląda się i buduje rodzaj optymalnej trasy tak, żeby rozpatrzeć sytuację („Czy ten czarny kulawy skurwiel z kretyńskim medalikiem na szyi przypadkiem nie podchodzi od strony krzaku forsycji ?”). Kot idzie trochę w lewo, trochę w prawo, eksperymentuje, potem w kolejnym dniu ustala trochę prostszą trasę, jednak – jakby niezadowolony ze zbytniego uproszczenia sytuacji – w kolejnych dniach znowu eksperymentuje, tylko nieco inaczej. Ten układ równań zachowywał się bardzo podobnie.
Staram się zrozumieć ten mechanizm, żeby go uogólnić. Najpierw potrzebne jest coś, co generuje błędy, na których można się uczyć czyli błędy, które określam dalej jako „znaczące” (w sensie, że mają znaczenie jako informacja). W tym układzie równań generowanie znaczących błędów polegało na połączeniu trzech elementów: składnika losowego, odchyleń generowanych przez składnik losowy oraz lokalnej pochodnej predykcji. Dobra, po kolei. Układ równań generował jakieś przewidywane wartości zmiennej wyjściowej – mojego igreka, czyli efektywności energetycznej gospodarki – na podstawie wartości pięciu zmiennych wejściowych: udziału zagregowanej amortyzacji (x1) oraz podaży pieniądza (x2) w PKB, średniej konsumpcji energii per capita (x3), udziału ludności miejskiej w populacji ogólnej (x4) oraz PKB per capita (x5). Dla wygody wklejam poniżej tabelę z danymi – dla Australii – na których eksperymentowałem.
Tabela 1 – Wybrane dane na temat efektywności energetycznej w Australii
Rok | PKB na jednostkę spożycia energii (USD PPP 2011 na kilogram równoważnika ropy naftowej) | Udział zagregowanej amortyzacji w PKB [%] | Udział podaży pieniądza w PKB [%] | Finalne spożycie energii per capita (tony równoważnika ropy naftowej) | Urban population as % of total population | GDP per capita, ‘000 USD |
y | X1 | X2 | X3 | X4 | X5 | |
1990 | 5,662020744 | 14,46 | 54,146 | 5,062 | 85,4 | 26,768 |
1991 | 5,719765048 | 14,806 | 53,369 | 4,928 | 85,4 | 26,496 |
1992 | 5,639817305 | 14,865 | 56,208 | 4,959 | 85,566 | 27,234 |
1993 | 5,597913126 | 15,277 | 56,61 | 5,148 | 85,748 | 28,082 |
1994 | 5,824685357 | 15,62 | 59,227 | 5,09 | 85,928 | 29,295 |
1995 | 5,929177604 | 15,895 | 60,519 | 5,129 | 86,106 | 30,489 |
1996 | 5,780817973 | 15,431 | 62,734 | 5,394 | 86,283 | 31,566 |
1997 | 5,860645225 | 15,259 | 63,981 | 5,47 | 86,504 | 32,709 |
1998 | 5,973528571 | 15,352 | 65,591 | 5,554 | 86,727 | 33,789 |
1999 | 6,139349354 | 15,086 | 69,539 | 5,61 | 86,947 | 35,139 |
2000 | 6,268129418 | 14,5 | 67,72 | 5,644 | 87,165 | 35,35 |
2001 | 6,531818805 | 14,041 | 70,382 | 5,447 | 87,378 | 36,297 |
2002 | 6,563073754 | 13,609 | 70,518 | 5,57 | 87,541 | 37,047 |
2003 | 6,677186947 | 13,398 | 74,818 | 5,569 | 87,695 | 38,302 |
2004 | 6,82834791 | 13,582 | 77,495 | 5,598 | 87,849 | 39,134 |
2005 | 6,99630318 | 13,737 | 78,556 | 5,564 | 88 | 39,914 |
2006 | 6,908872246 | 14,116 | 83,538 | 5,709 | 88,15 | 41,032 |
2007 | 6,932137612 | 14,025 | 90,679 | 5,868 | 88,298 | 42,022 |
2008 | 6,929395465 | 13,449 | 97,866 | 5,965 | 88,445 | 42,222 |
2009 | 7,039061961 | 13,698 | 94,542 | 5,863 | 88,59 | 41,616 |
2010 | 7,157467568 | 12,647 | 101,042 | 5,649 | 88,733 | 43,155 |
2011 | 7,291989544 | 12,489 | 100,349 | 5,638 | 88,875 | 43,716 |
2012 | 7,671605162 | 13,071 | 101,852 | 5,559 | 89,015 | 43,151 |
2013 | 7,891026044 | 13,455 | 106,347 | 5,586 | 89,153 | 43,238 |
2014 | 8,172929207 | 13,793 | 109,502 | 5,485 | 89,289 | 43,071 |
Obróbka danych z tabeli przez sieć neuronalną (układ równań) zaczynała się od czegoś, czego sens zrozumiałem dopiero zaobserwowawszy działanie sieci w praktyce i czego początkowo nie rozumiałem do końca: dla każdego iksa algorytm generował dwa kolejne iloczyny „x*współczynnik losowy”. Pierwszy raz to było w warstwie ukrytej sieci neuronalnej – tam było właśnie „x*współczynnik losowy” – drugi zaś raz w warstwie wyjściowej i tam było już „x*współczynnik losowy*zupełnie inny współczynnik losowy”. W oryginalnym algorytmie współczynniki losowe są generowane funkcją „random.rand” języka Python. Kiedy testowałem kolejne kroki algorytmu w Excelu, używałem zamiast tego funkcji „los()”.
Na początku tego nie rozumiałem. Potem mnie olśniło. Jestem kotem sąsiadów w moim ogródku, w słoneczny dzień i chcę się uwalić na dachu szopy, nie wdając się przy tym w (przesadną) awanturę. Próbuję przewidzieć ruchy innych kotów. Kiedy jestem zwykłym kotem, to eksperymentuję wyłącznie metodą rzeczywistych prób oraz ich równie rzeczywistych skutków. Kiedy jednak jestem kotem wyposażonym z inteligencję, wyobrażam sobie różne alternatywne sytuacje. Generowanie wartości hipotetycznych iksa, czyli „x*współczynnik losowy*zupełnie inny współczynnik losowy” to właśnie wyobraźnia. Mój układ równań wyobrażał sobie, jakie mogłyby być te iksy, gdyby im trochę wina dolać. W każdej rundzie iteracji na temat każdego rzeczywistego iksa powstawały dwie hipotezy na temat tego, czym (kim?) ten iks mógłby być przy odrobinie probabilistycznej fantazji.
Predykcja wartości oczekiwanych zmiennej y – dla wygody będę je dalej określał jako „y^” – odbywała się w oparciu o te hipotetyczne, wstrząśnięte wartości iksów, przy pomocy funkcji logistycznej, zwanej w informatyce funkcją sigmoidalną unipolarną. Wtedy, obserwując krok po kroku działanie algorytmu, zaobserwowałem coś, co dotychczas znałem tylko z teorii: przy tych konkretnych wartościach iksów, mój przewidywany igrek y^ był z reguły równy 1, a jego lokalna pochodna była w związku z tym równa zero. Innymi słowy mój neuron zwracał z reguły informację że coś się dzieje (sygnał wyjściowy „1”), ale w sumie to dzieje się rutynowo (zawsze „1”), więc w sumie nie ma co się podniecać i przesadnie aktywować (pochodna „0”).
Innymi słowy, moje y^ były prawie zawsze równe 1, czyli oczywiście różne od rzeczywistych igreków z tabeli, a lokalne pochodne y^’ były prawie zawsze równe „0”. Lokalny błąd estymacji był prawie zawsze równy „y minus 1”. Prawie nie oznacza jednak naprawdę zawsze. W warstwie ukrytej sieci, czyli tam gdzie iksy miały tylko trochę wyobraźni, nie działo się nic. Jednak w warstwie wyjściowej, gdzie iksy już były dwukrotnie pomnożone przez czynnik losowy, pojawiały się drobne, lokalne odchylenia sigmoidu od „1” i ty samym, w niektórych latach z mojego okienka obserwacji 1990 – 2014, pojawiały się lokalne pochodne tego sigmoidu trochę większe od zera. W niektórych latach lokalny błąd estymacji wynosił więc „y minus trochę mniej niż 1” i niezerowa pochodna oznaczała, że coś się dzieje.
To jest stara, jeszcze Newtonowska interpretacja pochodnej funkcji. Wszystko co się dzieje, jest funkcją pochodną od jakiejś innej funkcji, czyli wszystko co się dzieje jest zmianą wartości jakiejś całki. Kiedy idę, zmienia się moja odległość od jakiegoś punktu. Odległość to całka mojego marszu, a tempo w jakim maszeruję to pochodna od niej.
W tym konkretnym algorytmie założono, że dla każdej lokalnej predykcji y^ obliczamy współczynnik znaczenia tego błędu jako iloczyn odchylenia od wartości rzeczywistej igreka, czyli (y – y^), pomnożonej przez pochodną wartości przewidywanej. W sumie to jest (y – y^)*y^’. Chwilę mi zajęło, zanim zrozumiałem logikę tej metody. Błąd ma znaczenie tylko wtedy, kiedy – matematycznie rzecz biorąc – coś się dzieje, czyli kiedy pochodna wartości przewidywanej jest różna od zera. Pochodna (nieznacznie) różna od zera pojawia się wtedy, kiedy wartość oczekiwana y^ jest nieznacznie mniejsza od 1. Innymi słowy, neuron uznaje że coś się dzieje kiedy nie ma podstaw do wygenerowania pełnego sygnału wyjściowego równego 1, tylko sygnał nieznacznie słabszy. W sumie, ten neuron oparty na funkcji sigmoidalnej reaguje, w sensie że czegoś się na serio uczy wtedy, kiedy jest jakby odcięty od pełnego sygnału.
Jest więc tak, w tym algorytmie, że dwukrotnie wstrząsnąwszy moje oryginalne iksy przy pomocy dwóch kolejnych mnożeń przez czynnik losowy, w niektórych latach otrzymuję sytuację na tyle niepokojącą dla mojej sieci neuronalnej, że generuje lokalne niezerowe pochodne, a więc lokalne znaczące błędy. Po pierwszej rundzie obliczeń – czyli pierwszej iteracji mojej funkcji – zaczynam wykorzystywać te błędy. W żargonie informatycznym mówimy wtedy o wstecznej propagacji błędu. Matematycznie to wygląda tak (w tym konkretnym przypadku), że do moich już wstrząśniętych iksów w rundy pierwszej dodaję lokalne błędy wygenerowane w rundzie pierwszej. Wyliczam więc „x*współczynnik losowy*[zupełnie inny współczynnik losowy + (y – y^)*y^’ z poprzedniej rundy]”. Moje wyobrażone, alternatywne stany rzeczywistości koryguję o wnioski z poprzedniej rundy eksperymentu.
W większości lokalnych zdarzeń z mojej wyjściowej tabeli na temat Australii te wnioski są zerowe, tylko w niektórych przypadkach wyrażenie (y – y^)*y^’ daje coś tam nieznacznie różnego od zera. Algorytm jest skonstruowany tak, żeby te pojedyncze, znaczące błędy wykorzystywać jako materiał do nauki.
W podstawowym arkuszu Excela, gdzie rozpisałem sześć rund powtarzania tego algorytmu sieci neuronowej (neuronalnej ? a cholera wie), losowe wartości wag są ciągle losowe. Przy każdym otwarciu pliku losują się na nowo. Zrobiłem trzy „zamrożone” wersje tych wag, czyli zaraz po ich rozlosowaniu zmieniłem funkcje w komórkach na sztywne wartości. Zrobiłem trzy koty sąsiadów, które na raz wchodzą do mojego ogródka. Dla każdego z nich Australia z tabeli powyżej to jak ścieżka na dach szopy na narzędzia. Każdy kot to kot, czyli ma powtarzalną strukturę (no, może z wyjątkiem tego z wisiorkiem na szyi). Każdy kot wchodzi jednak do mojego ogródka ze swoimi przekonaniami i uprzedzeniami. Dla jednego z nich koty szare są zawsze wyżej postawione od tych czarnych. Oczywiście, on jest szary. Dla drugiego dokładnie odwrotnie. Jeden z nich wyznaje religię słońca na niebie, inny religię michy z karmą Purina, a jeszcze inny jest ateistą, czyli torba z jedzeniem to torba z jedzeniem, a nie żadna tam łaska z wysoka.
No więc te trzy koty wchodzą do mojego ogródka. Każdy z nich startuje z innego punktu (inny rozkład losowych współczynników tworzących wyobraźnię kota), jednak każdy z nich uczy się według tego samego schematu, w sześciu rundach. Tabela 2, poniżej, rozpisuje tą metaforę w sposób analityczny. Dla każdej z sześciu rund rozpisałem lata, w których algorytm generuje znaczące lokalne błędy, do propagacji wstecznej. W każdej rundzie wpisałem skumulowaną wartość lokalnych błędów. Widać, że pierwsze 5 rund każdy z kotów przechodzi inaczej. W szóstej stają się prawie identyczne.
Tabela 2
Losowe rozkłady początkowych wag dla wartości “x” | |||
Kolejne rundy (iteracje) algorytmu | Rozkład 1 | Rozkład 2 | Rozkład 3 |
Runda 1 | |||
Lata wykazujące znaczące błędy, do propagacji wstecznej | 1999; 2002; 2003; 2006 | 1990; 1994; 1998 – 99; 2009; 2012 – 13; | 1990 – 91; 1996 – 97; 1999; 2001; 2007; 2009 – 11; |
Skumulowana wartość błędów | 5,53241E-09 | 7,0537E-05 | 0,000916694 |
Runda 2 | |||
Lata wykazujące znaczące błędy, do propagacji wstecznej | 1992; 1993; 1999; 2002; 2006 | 1996; 1999; 2006; 2012 – 13; | 1991 – 92; 1996; 2004; 2007; 2011; |
Skumulowana wartość błędów | 6,45047E-12 | 2,93896E-07 | 0,035447255 |
Runda 3 | |||
Lata wykazujące znaczące błędy, do propagacji wstecznej | 1990; 1996; 1999; 2002; 2006 | 1991; 1997; 1999; 2010; 2012 – 14 | 1991; 1996; 1999; 2002 – 2004; 2007; 2009 – 2012; |
Skumulowana wartość błędów | 2,34651E-13 | 4,39246E-06 | 0,00056026 |
Runda 4 | |||
Lata wykazujące znaczące błędy, do propagacji wstecznej | 1990 – 92; 1994 – 95; 1997; 2001 – 2002; 2006 – 2007; 2012 | 1990; 1992; 1996; 1999; 2012 – 13; | 1990 – 91; 1994 – 96; 1999; 2007; 2009 – 11; |
Skumulowana wartość błędów | 0,000171883 | 0,000741233 | 6,27817E-05 |
Runda 5 | |||
Lata wykazujące znaczące błędy, do propagacji wstecznej | 1993; 1999; 2002; 2003; 2006 | 1997; 1999; 2007; 2012 – 13; | 1990 – 91; 1996; 2003; 2007 – 2009; 2011; |
Skumulowana wartość błędów | 3,46206E-05 | 0,000548987 | 0,001532496 |
Runda 6 | |||
Lata wykazujące znaczące błędy, do propagacji wstecznej | 1991 – 94; 1996 – 97; 2000; 2005; 2007; 2013 | 1991 – 94; 1995 – 96; 2000; 2005; 2007; 2013; | 1991 – 94; 1996 – 97; 2000; 2005; 2007; 2013 |
Skumulowana wartość błędów | 3,07871E-08 | 3,07871E-08 | 3,07871E-08 |
A więc tak działa jedna z wielu możliwych form inteligencji: niezależnie od wyjściowych poglądów na BARDZO WAŻNE SPRAWY (no bo kto by się tam wysilał na inteligencję w sprawach błahych ), w skończonej liczbie kroków ta forma inteligencji tworzy spójny system uczenia się.
Tyle tej prawie nowej metody naukowej na dzisiaj. Serio, prawie nowa, mało jeżdżona.