Nie przejmuj się, mój perceptronku

 

Mój wstępniak na You Tube

 

A takie tam różne. Robię trochę badań nad zjawiskiem inteligencji zbiorowej i ponieważ trudno mi tak z biegu znaleźć dobre przykłady inteligencji zbiorowej w prawdziwym życiu, to zrobiłem sobie taki mały perceptron – czyli sieć neuronalną – który ma zastępować prawdziwe życie. Tak trochę w kontekście tej inteligencji zbiorowej, dalej przegryzam się przez Rozporządzenia Ministra Nauki i Szkolnictwa Wyższego z dnia 22 lutego 2019 roku w sprawie ewaluacji jakości działalności naukowej. Ogólnie się dzieje.

Interesuję się inteligencją zbiorową, ponieważ mam wrażenie, że nawet najdziwniejsze relacje międzyludzkie, takie na pozór naprawdę pogięte, mają jakiś swój funkcjonalny sens. Robimy coś na pierwszy rzut oka idiotycznego. Kiedy jednak pierwszy rzut oka ustępuje miejsca drugiemu, wtedy ten drugi widzi trochę inny kontekst i widzi sens w tym pozornym idiotyzmie. Weźmy takie przejście dla pieszych. Gdyby przyglądał się temu człowiek z dżungli, stwierdziłby: „Idiotyzm. Chodzą zawsze tak samo, po ścieżkach oznakowanych tak, że tylko niewidomy tapir by nie zauważył. Są przewidywalni jak fiks. Nic nie upolują. Zdechną z głodu. Przykre”. A jednak my nie zdychamy z głodu i te przejścia dla pieszych jakoś działają, tyle że w innym kontekście niż dżungla.

Kiedy używam sztucznej inteligencji, odkrywam mnóstwo ciekawych rzeczy na temat działania naszej inteligencji. Znacie zapewne tą zasadę, że nagrody działają na nas mocniej niż kary. Wzmocnienie pozytywne działa mocniej niż wzmocnienie negatywne. No i ja tu właśnie prowadzę badania na temat efektywności energetycznej gospodarek, w sensie na temat ilości PeKaBe (Produktu Krajowego Brutto) w przeliczeniu na 1 kilogram przeliczeniowy ropy naftowej w sensie finalnej konsumpcji energii, no i zrobiłem taki prosty perceptron wielowarstwowy (zaraz tam „wielo-„… trzy warstwy wszystkiego), no i ten perceptron kombinuje jak łączyć tą efektywność energetyczną z PeKaBe na głowę, z odsetkiem energii odnawialnych w całości konsumpcji finalnej i z różnymi innymi zjawiskami. W zasadzie, perceptron wielowarstwowy uczy się na podstawie porażek i związanych z tym kar. Perceptron się multiplikuje i próbuje, jaka mu wyjdzie efektywność energetyczna z tej jego chwilowej postaci. Jak się multiplikuje, to się nieuchronnie myli, ale umie się do błędu przyznać, a nawet mierzy ten błąd i następnie go wpuszcza do wartości swoich zmiennych wejściowych. Perceptron uczy się czegokolwiek tylko wtedy, kiedy się pomyli i w pewnym sensie karze sam siebie za pomyłkę.

Postanowiłem spróbować co będzie, kiedy dam mojemu perceptronowi wzmocnienie pozytywne. Dzielę błędy na korzystne i niekorzystne. Te pierwsze to efektywność energetyczna wyższa od oczekiwanej, te drugie to wręcz przeciwnie. Kiedy perceptron popełnia błąd korzystny, wtedy pozwalam mu wykorzystać go w całości w kolejnych rundach eksperymentalnych. Kiedy jednak popełnia błąd niekorzystny, to ja ten błąd zmniejszam i dopiero taki zmniejszony jest propagowany dalej. Wzmacniam wydźwięk sukcesów, zmniejszam wagę porażek.

No i co? No i w sumie to kicha. Mój standardowy eksperyment z perceptronem to 5000 prób, no i zawsze coś z tego wychodzi. Kiedy jednak wprowadzam do algorytmu mechanizm wzmacniania sukcesów i osłabiania porażek – czyli kiedy wprowadzam arbitralne rozróżnienie na sukcesy i porażki – to perceptron już po 30 ÷ 40 rundach eksperymentalnych stwierdza, że zjadł wszystkie rozumy. Przestaje generować błąd, fiksuje wszystkie zmienne i wszystko byłoby fajnie, gdyby ich nie fiksował na poziomach absurdalnie niskich. Wyszło mu, na ten przykład, że Austriacy mogliby mieć taką samą efektywność energetyczną, jak mają teraz, przy liczbie ludności o ¾ niższej od obecnej i generalnie ze wszystkim co się da o ¾ poniżej poziomów dzisiejszych. Jak mu dałem wzmocnienie pozytywne, to doradził Austriakom powrót do prymitywu. Dobrze, że mu nie wrzuciłem danych na temat Polski. Dopiero by wstyd był, a tak to co najwyżej może być incydent dyplomatyczny.

Jeszcze parę słów wyjaśnienia, dlaczego i jak stosuję perceptron wielowarstwowy, aby przedstawić działanie inteligencji zbiorowej. Opieram się na tzw. teorii roju (patrz np. Stradner et al 2013[1]), zgodnie z którą inteligencja zbiorowa przejawia się jako koordynacja. W ten sposób ograniczamy nieco nasze ambicje względem naszej własnej zbiorowej inteligencji. Fajnie byłoby myśleć, że jako zbiorowość dążymy do jakiegoś sensownego zbiorowego celu i robimy to rozsądnie. Zauważmy jednak, że ocena wartości naszego celu oraz ocena racjonalności drogi, którą przyjęliśmy wymagają odniesienia etycznego i prakseologicznego. Układ odniesienia musimy oczywiście stworzyć sami. Nikt inny tego za nas nie zrobi. Jeżeli sami tworzymy układ odniesienia dla oceny naszych celów i ścieżki do ich osiągnięcia, to tak trochę mało ambitnie. Sami sobie kadzimy, do jakich to fajnych celów dążymy albo też sami siebie wbijamy w kompleksy, że niby te nasze cele za mało ambitne.

W teorii roju zakłada się że wystarczy, jak jesteśmy skoordynowani. Możemy być skoordynowani na trzech poziomach: statycznym, dynamicznym skorelowanym i dynamicznym losowym. Koordynacja statyczna to tak, jak w synapsach albo w McDonaldzie. Jak podmiot A zrobi X, to podmiot B zawsze zrobi Y. Zawsze tak samo. Koordynacja dynamiczna skorelowana jest wtedy, jak podmiot B może zachować się różnie wobec działania podmiotu B, ale to różnie ma swój powtarzalny wzorzec. B czasami zrobi Y1, a czasami Y2, ale Y3 to już raczej nie, a to czy Y1 czy Y2 zależy od subtelnych różnic w zachowaniu X ze strony podmiotu A. Kiedy jednak podmiot A nawet nie wie, który inny podmiot zareaguje na jego zachowanie A, wtedy musi to zachowanie jakoś zasygnalizować szerszej zbiorowości. Może zareaguje B, a może C? Kto wie? Tak działa roznoszenie zarodników roślin i grzybów, czy też rozprzestrzenianie feromonów.  To jest koordynacja dynamiczna losowa.

Rój jest inteligentny, kiedy potrafi przystosować się do nowych okoliczności poprzez zmianę stopnia koordynacji. Eksperymentujemy z nowymi wariantami naszych nawyków i strategii. Na chwilę przechodzimy od koordynacji statycznej do dynamicznej skorelowanej. Ta ostatnia może się na chwilę przekształcić w koordynację dynamiczną losową. Wszystko po to, żeby wpuścić trochę świeżego powietrza i nauczyć się nowych wzorców zachowań. Nowe wzorce zachowań będą prawdopodobnie faworyzować nieco inny typ partnerów seksualnych, a tym samym nieco inną pulę genów. W ten inteligencja przekłada się na trwałą mutację genetyczną całego gatunku.

Sieć neuronalna wykorzystuje zbiór zmiennych. Kiedy są to zmienne ekonomiczne, takie jak PKB na głowę mieszkańca czy efektywność energetyczna, każdą z nich można traktować jako chwilowy przejaw zbioru decyzji podejmowanych przez dużą liczbę ludzi w dłuższym okresie czasu. Stopień wzajemnego powiązania tych decyzji można traktować jako stopień wzajemnej koordynacji zachowań, które stoją za takimi a nie innymi wartościami zmiennych. Związki pomiędzy liczbami są prostsze niż związki między ludźmi. Można te związki zmierzyć, np. przy pomocy odległości Euklidesowej: V(x1;x2) = [(x1 – x2)2]0,5.  Z tym pierwiastkiem z kwadratu to chodzi o zlikwidowanie możliwego minusa. Ujemna odległość źle wygląda w pomiarach. W tym wypadku należy pohamować zapędy wpojone na lekcjach matematyki. Mówili nam „upraszczaj !”. W tym wypadku, jeżeli uproszczę potęgi, to wyjdzie mi potęga „1”, a to z minusem nic nie zrobi. Nie upraszczamy więc nadmiernie, tylko grzecznie, po kolei, najpierw do kwadratu, a potem dopiero z tego pierwiastek kwadratowy.

Kiedy mam wiele zmiennych, wtedy dla każdej z nich mogę określić jej odległości Euklidesowe wobec wszystkich pozostałych i wyciągnąć średnią arytmetyczną z tych odległości. Potem z tych średnich arytmetycznych wyciągam kolejną średnią arytmetyczną i otrzymuję w ten sposób jedną z możliwych postaci funkcji spójności, która określa jak ciasne są relacje między zmiennymi.

Moje eksperymenty z perceptronem wielowarstwowym pokazały, że zmienia on wartość swojej funkcji spójności, a dokładnie zwiększa średnią odległość Euklidesową między swoimi zmiennymi wejściowymi po to, żeby zminimalizować błąd w szacowaniu zmiennej wyjściowej (wynikowej). W tym sensie perceptron zachowuje się jak inteligentny rój żywych istot. Daje sobie trochę dodatkowego luzu, żeby się czegoś nauczyć. Co ciekawe, kiedy w skład błędu odnotowywanego przez perceptron włączam także wartość jego funkcji spójności – czyli wzajemną odległość Euklidesową zmiennych – wtedy sieć neuronalna zachowuje się bardzo rożnie w zależności od zastosowanej funkcji aktywacyjnej. Kiedy stosuję funkcję sigmoidalną unipolarną, wtedy perceptron mniej się uczy i zachowuje większą spójność. Zachowuje się trochę jak człowiek, któremu nagle kazano zwracać uwagę na nieznane mu wcześniej zasady savoir-vivre’u. Zachowywać to się zachowuje, ale jakby tak z lekkim odcieniem kija w d**ie. Kiedy jednak zmieniam funkcję aktywacji na tangens hiperboliczny, wtedy perceptron zmuszony do obserwacji swojej własnej spójności zaczyna się bujać w krainę wyobraźni: znacznie zmniejsza swoją wewnętrzną spójność i wymyśla bardzo wysokie wartości zmiennych wejściowych.

No i ja tu tak gadu gadu i ni z tego, ni z owego prawie dokładnie wyjaśniłem, o co mi chodzi z tym perceptronem jako wyrazem inteligencji zbiorowej. Mam coś, co potrafi samo ze sobą eksperymentować i stopniowo zmniejszać błąd generowany przez te eksperymenty. Znaczy będą z tego czegoś ludzie. Jak się uczy, to rozluźnia swoją wewnętrzną spójność, czyli otwiera się na inherentny chaos egzystencji. Otwiera się różnie w zależności od zastosowanej funkcji aktywacyjnej oraz tego, czy ma zwracać uwagę na to, jak się otwiera. Jak pozwolę temu czemuś odróżniać sukcesy od porażek i wyciągać optymistyczne wnioski z niektórych odchyleń od wartości oczekiwanych, to nagle stwierdza, że już wszystko wie i że dość tego eksperymentowania.

 

No dobrze. Teraz ważne pytanie: co z tego? Jaka jest wartość naukowa tego narzędzia? Przypuszczam, że mogę w ten sposób tworzyć dość dokładne hipotezy dla badań empirycznych. Przy pomocy perceptrona symuluję, jakie mogą być oczekiwane wartości zmiennych empirycznych, w różnych warunkach uczenia się. Następnie sprawdzam dopasowanie wartości empirycznych do tych oczekiwanych i mogę określić, która hipoteza na temat zbiorowego uczenia się jest najbliższa prawdy.

 

Perceptron opiera się na akumulacji danych z przeszłości. To typ sztucznej inteligencji, który dokłada kolejne porcje informacji do już posiadanej wiedzy i w ten sposób coś optymalizuje. To jest mechanizm uczenia się, który wykracza poza potocznie pojmowany schemat « bodziec <> reakcja » (już raz o tym pisałem, w „Dwie kule w Kalifornii i wielebny Thomas Bayes”). W tej chwili chyba najbardziej fascynuje mnie to, w jaki sposób modyfikacja tej pamięci – czy też raczej zmiana sposobu zbierania informacji do pamięci – wpływa na proces uczenia się. Przedstawię teraz krótki eksperyment, jaki prowadzę przy pomocy mojego perceptronu, na danych dotyczących efektywności energetycznej gospodarki Polski.

 

Mój podstawowy model, który wykorzystałem już do jednego artykułu, zakłada że efektywność energetyczna gospodarki jest efektem oddziaływania następujących zmiennych:

 

  1. Współczynnika kapitału w bilansowych środkach trwałych przedsiębiorstw w przeliczeniu na 1 krajowe zgłoszenie patentowe; to nam mówi, jak przeciętnie dokapitalizowany jest każdy wynalazek zgłaszany do patentowania;
  2. Współczynnika udziału zagregowanej amortyzacji środków trwałych w PKB; ta zmienna mierzy, jakim obciążeniem dla gospodarki jest nadążanie za zmianą technologiczną;
  3. Liczby krajowych zgłoszeń patentowych na 1 mln mieszkańców, czyli stopnia innowacyjności społeczeństwa;
  4. Finalnego zużycia energii per capita;
  5. Odsetka ludności miejskiej w całości populacji;
  6. Zagregowanego PKB
  7. PKB per capita
  8. Liczby ludności

 

Biorę jedną i tą samą funkcję aktywacji neuronalnej: sigmoid unipolarny. Jako materiał wyjściowy, żeby perceptron oparty na tej funkcji cośkolwiek zakumał, daję mu dane empiryczne na temat powyższych zmiennych w Polsce, włącznie z samą efektywnością energetyczną, dla lat 1990 – 2014. Dane standaryzuję, czyli dla każdej zmiennej każdą wartość z osobna dzielę przez zaobserwowaną w jej rozkładzie wartość maksymalną. W efekcie każda zmienna ma wartości w przedziale 0 < x ≤ 1. Zaczynamy w roku 1990. Perceptron generuje wagi losowe, w przedziale 0 < w < 1 dla każdej zmiennej. Mnoży zestandaryzowane wartości zmiennych przez te wagi losowe i suma iloczynów „xi*wi” stanowi złożony argument „x” dla funkcji sigmoidalnej unipolarnej. Funkcja zwraca jakąś lokalną wartość i jakąś lokalną pochodną, dla roku 1990. Biorę rzeczywistą, zestandaryzowaną wartość zmiennej wynikowej – efektywności energetycznej dla roku 1990 – i odejmuję od niej wartość zwróconą przez funkcję aktywacji. Tą różnicę mnożę przez lokalną pochodną i mam lokalny błąd ważony znaczeniem lokalnej zmiany wartości (czyli pochodnej).

 

Swoją drogą, na boku, obliczam średnią arytmetyczną dla odległości Euklidesowych każdej zmiennej, w roku 1990, od wszystkich pozostałych. Potem z tych średnich arytmetycznych wyciągam kolejną średnią arytmetyczną, a następnie obliczam jej odwrotność, czyli 1/V(x). To jest ogólna funkcja spójności mojego perceptronu.

 

Dalej, począwszy od roku 1991 i dalej w przyszłość, mam jeden stały sposób postępowania – czyli podstawowy schemat uczenia się mojego perceptronu – oraz kilka możliwych wariacji na ten główny temat. Co do zasady, w roku 1991, perceptron bierze zestandaryzowaną wartość każdej zmiennej, zarówno wynikowej efektywności energetycznej i jak i zmiennych wejściowych (a – h), dodaje do tej empirycznej wartości te lokalny błąd ważony pochodną dla roku poprzedniego – 1990 – i dalej tak samo, czyli wagi losowe, suma iloczynów zmiennych wejściowych przez wagi losowe jako złożony argument dla funkcji aktywacji neuronalnej itd. To się nazywa propagacja błędu.

 

W ten sposób, dla każdego roku od 1991 do 2014, perceptron buduje stopniowo coraz bardziej odchyloną od rzeczywistości wersję wydarzeń. Daje to 2014 – 1990 = 24 rundy eksperymentalne, podczas których perceptron twórczo uczy się na danych empirycznych.

Począwszy od rundy 25 i dalej, perceptron już nie ma danych empirycznych jako układu odniesienia. Wtedy zaczyna powtarzać tą samą operację na swojej własnej, zmodyfikowanej wersji roku 2014. Nadbudowuje na tym ostatnim rzeczywistym zdarzeniu swoją całkowicie własną wersję wydarzeń, wciąż według tego samego mechanizmu dodawania błędu zaobserwowanego w bezpośrednio poprzedzającej przeszłości.

 

Teraz możliwe modyfikacje. Pierwsza polega na obserwacji własnej spójności. W każdej kolejnej rundzie eksperymentu, poza dodawaniem błędu z poprzedniej rundy, perceptron dodaje średnią spójność tej konkretnej zmiennej z pozostałymi zmiennymi (jej średnią odległość Euklidesową od pozostałych zmiennych), zaobserwowaną w rundzie poprzedniej. Innymi słowy, perceptron zapamiętuje, jak daleko była ta konkretna zmienna od pozostałych. Potem jak w scenariuszu podstawowym: wagi losowe, suma iloczynów itede.

 

Druga modyfikacja to rozróżnienie na sukcesy i porażki. Obliczam, o ile wzrosła standaryzowana efektywność energetyczna gospodarki Polski w okresie 1990 – 2014. Wychodzi (11,77694401 – 9,673094195) / 11,77694401 = 0,178641404. Dalej dzielę to przez 24 lata, czyli 0,178641404 / 24 = 0,007443392. To jest próg odniesienia „y*” dla odróżnienia sukcesu od porażki. Błąd „e” zwracany przez funkcję aktywacji perceptronu i spełniający warunek „e > y*” jest sukcesem, każda inna jest porażką. Daję więc mojemu perceptronowi do zrozumienia: jeżeli wygenerowałeś błąd większy od średniego rocznego przyrostu efektywności energetycznej, to brawo. Jak nie, to niestety, ale wtopiłeś.

Teraz pytanie, jak mu to powiedzieć. Niektórzy ludzie źle reagują na krytykę. Nie wiem, jak zareaguje sztuczna inteligencja. Muszę sprawdzić. Na początek robię tak, jak doradza psychologia: zamiast krytykować, pokaż konsekwencje sukcesu oraz porażki. Mogę te konsekwencje wzmocnić w jednym albo w drugim kierunku, czyli wzmocnić sukcesy kosztem porażek („nieważne, że nie zająłeś pierwszego miejsca, perceptronku, ten przed Tobą i tak był na koksach, ważne jest uczestnictwo i rywalizacja”) albo też mogę wzmocnić porażki kosztem sukcesów („Ty łajzo, jak mogłeś tak polec!”). Kiedy chcę wzmocnić pamięć o sukcesach i osłabić pamięć o porażkach, to robię na przykład tak, że kiedy e > y*, to wtedy pozwalam perceptronowi dalej propagować błąd „e”, kiedy jednak e ≤ ∆y*, to wtedy pozwalam propagować tylko jakiś ułamek błędu „e”. Kiedy chcę wzmocnić pamięć o porażkach kosztem pamięci o sukcesach, to robię odwrotnie. Wszystko to mogę ująć elegancko w ramy jednego warunku logicznego:

 

>> jeżeli (e > y*), to propaguj ß1*e, a jeżeli (e ≤ y*), to propaguj ß2*e.

 

Parametry ß1 oraz ß2 określają odpowiednie wzmocnienie pamięci o sukcesach oraz o porażkach. Jeżeli ß1 > ß2, to wzmacniam wspomnienie sukcesu („W 1970-tym tośmy im pokazali!”). Jeżeli ß1 < ß2, wzmacniam z kolei wspomnienie porażki („Nigdy nie zapomnimy tego, jak ONI nas skrzywdzili!”). Konstruuję pięć scenariuszy:

 

Scenariusz 0: Bez uczenia się na spójności, bez nagrody

Scenariusz A.1: Bez uczenia się na spójności, wzmocnienie nagrody *2 ó ß1 = 1, ß2 = 0,5

Scenariusz A.2: Bez uczenia się na spójności, wzmocnienie nagrody o 5% ó ß1 = 1, ß2 = 0,95

Scenariusz B.1: Uczenie się na spójności, wzmocnienie nagrody *2 ó ß1 = 1, ß2 = 0,5

Scenariusz B.2: Uczenie się na spójności, wzmocnienie nagrody o 5% ó ß1 = 1, ß2 = 0,95

 

Dla każdego z pięciu scenariuszy obserwuję funkcję spójności mojego perceptronu – czyli to, na ile zachowuje się jak inteligentny rój – oraz procent energii odnawialnych w konsumpcji finalnej energii ogółem. Obserwacja obejmuje 100 rund eksperymentalnych: interesuje mnie przede wszystkim to, jak perceptron zaczyna się uczyć. Tak, dla perceptrona wielowarstwowego 100 rund eksperymentalnych to zaledwie rozgrzewka. To jest chyba największa odmiana w stosunku do moich poprzednich eksperymentów z tym perceptronem. Wcześniej moja standardowa obserwacja obejmowała 5000 rund eksperymentalnych, czyli okres 50 razy dłuższy (dłuższy w sensie zgromadzonego doświadczenia, a nie upływu czasu – perceptron nie ma pojęcia czasu). Ten eksperymentalny zoom przyniósł bardzo ciekawe wyniki, które pokazuję na dwóch wykresach poniżej i komentuję dalej, pod wykresami.

 

Widać wyraźnie dwa etapy uczenia się perceptronu. Pierwszy etap to ten, kiedy perceptron pracuje na danych empirycznych i tylko trochę je tak sobie przekształca. W tym etapie wszystkie pięć scenariuszy uczenia się idzie łeb w łeb. Wszystkie pięć zmniejsza wartość funkcji spójności – a więc usztywnia strukturę logiczną i czyni ją mniej podatną na zmiany adaptacyjne – a jednocześnie zwiększa udział energii odnawialnych w ogólnej konsumpcji. W momencie kiedy perceptron przechodzi we fristajl, czyli kiedy już sam buduje wartości danych wejściowych na podstawie zapamiętanych informacji o rzeczywistości, wszystko się zmienia. Scenariusz 0 oraz scenariusze A.1 i A.2 utrzymują sztywną strukturę logiczną (niską wartość funkcji spójności) oraz mniej więcej ten sam mix energetyczny, w sensie względem odnawialnych. Z kolei scenariusze B.1 i B.2 wracają częściowo do poprzedniej, nieco luźniejszej struktury logicznej perceptronu (wartość funkcji spójności zwiększa się), a jednocześnie generują coraz większy odsetek energii odnawialnych.

 

Kiedy więc robię zbliżenie na 100 pierwszych rund eksperymentalnych, obserwuję coś przeciwnego niż relacje zaobserwowane dla 5000 rund. Perceptron wyposażony w zdolność uczenia się na swojej własnej spójności zachowuje się jak o wiele bardziej inteligentny rój niż perceptron bez takiej zdolności. Przy 5000 rund perceptron obserwujący własną spójność wydawał się mieć kij w tyłku, a tutaj, przy zbliżeniu na pierwsze 100 rund, jest dokładnie odwrotnie: jest bardziej elastyczny i jakby śmielszy i więcej fantazji ma i w ogóle. Co ciekawe, perceptron operujący stosunkowo niewielkim rozróżnieniem między karą a nagrodą  –  jeżeli (e > y*), to propaguj e, a jeżeli (e ≤ y*), to propaguj 0,95*e – buja się bardziej niż ten, który operuje rozróżnieniem jak jeden do dwóch.

 

Najważniejszym czynnikiem różnicującym procesy uczenia się okazuje się być obserwacja własnej spójności. Jeżeli traktuję perceptron jako przedstawienie rzeczywiście działającej inteligencji zbiorowej, społeczność wyposażona w zdolność wyciągania wniosków z własnej spójności może nauczyć się więcej, niż społeczność pozbawiona takiej zdolności. No jednak musi być jakieś jednakowoż. Jednakowoż, kiedy sięgam do rundy eksperymentalnej 5000, obraz się zmienia. Perceptron wyposażony w zdolność obserwacji własnej spójności generuje odsetek energii odnawialnych ok. 16,5%. Z kolei ten, który ignoruje własną spójność osiąga ok. 17,5%. Coś się zmienia gdzieś po drodze między setną rundą eksperymentalną, a tą pięciotysięczną. Jest taka masa krytyczna pamięci zgromadzonej przez perceptron, która w pewnym sensie odwraca jego działanie. Ciekawe.

[1] Stradner, J., Thenius, R., Zahadat, P., Hamann, H., Crailsheim, K., & Schmickl, T. (2013). Algorithmic requirements for swarm intelligence in differently coupled collective systems. Chaos, Solitons & Fractals, 50, 100-114.

Trzy koty w moim ogródku i sieć neuronalna

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.