Sztuczna inteligencja/SI Moduł 12

Z Studia Informatyczne
Wersja z dnia 09:26, 5 wrz 2023 autorstwa Luki (dyskusja | edycje) (Zastępowanie tekstu – „,</math>” na „</math>,”)
Przejdź do nawigacjiPrzejdź do wyszukiwania

Sieci neuronowe - wprowadzenie

Sieci neuronowe są jedną z wielu możliwych realizacji aproksymatora regresyjnego. Swoją popularność zawdzięczają w pewnej mierze analogiom biologicznym – można w nich upatrywać niezwykle uproszczonych modeli naturalnych struktur neuronowych. Przykładem sieci neuronowych, chyba najbardziej rozpowszechnionym, jest perceptron wielowarstwowy, któremu poświęcimy tę lekcję. Inne rozpowszechnione struktury sieci neuronowych to architektury ze sprzężeniem zwrotnym (tzw. sieci Hopfielda), realizujące układy dynamiczne, oraz architektury samoorganizujące (tzw. sieci Kohonena), realizujące algorytm grupowania. Tematyka sieci neuronowych jest dość dobrze opisana w literaturze uzupełniającej, do której odsyłamy bardziej zainteresowanych. Poniżej naszkicujemy podstawowe zagadnienia związane z wykorzystaniem perceptronu jako metody regresji oraz z metodami uczenia się stosowanymi dla tej sieci.

Definicja perceptronu wielowarstwowego

Perceptron wielowarstwowy jest aproksymatorem nieliniowym RnRm. Graficzna postać sieci jest następująca:

Węzeł grafu sieci odpowiada pojedynczemu neuronowi. Krawędź odpowiada połączeniu między neuronami (tzw. połączenie synaptyczne) – jest skierowana od wyjścia jednego do wejścia drugiego neuronu, co odpowiada jednokierunkowemu przepływowi danych.

Neuron działa w taki sposób, że dokonuje się ważonego sumowania wartości wejść, obliczając wartość, zwaną pobudzeniem Parser nie mógł rozpoznać (błąd składni): {\displaystyle h_i\} ,:

Parser nie mógł rozpoznać (błąd składni): {\displaystyle h_i = \sum_{j=1..n} w_{ij} x_j + w_{i0}\} ,

Wygodnie jest założyć, że neuron otrzymuje jeszcze jedno wejście Parser nie mógł rozpoznać (błąd składni): {\displaystyle x_0\} , o wartości równej stale jedynce. Przy takim założeniu, pobudzenie da się zapisać prościej jako

Parser nie mógł rozpoznać (błąd składni): {\displaystyle h_i = \sum_{j=0..n} w_{ij} x_j\} ,

Wyjście neuronu powstaje w wyniku podania pobudzenia na funkcję aktywacji Parser nie mógł rozpoznać (błąd składni): {\displaystyle g\} ,:

Parser nie mógł rozpoznać (błąd składni): {\displaystyle y_i = g(h_i)\} ,

W przypadku sieci neuronowych, funkcja aktywacji ma kształt litery „s” – jest monotonicznie rosnąca z asymptotami poziomymi w nieskończonościach. Najczęściej przyjmuje się funkcję tangens hiperboliczny:

g(x)=tanh(x)=exexex+ex

przyjmującą wartość z zakresu Parser nie mógł rozpoznać (błąd składni): {\displaystyle \bf [–1,1]} , względnie funkcję logistyczną:

g(x)=11+ex

o wartościach z zakresu [𝟎,𝟏].

Neurony zgrupowane są w warstwy w taki sposób, że między neuronami tej samej warstwy nie ma połączeń, a połączenia występują jedynie między neuronami sąsiadujących warstw.

Wyróżnia się warstwę neuronów wyjściowych (zwaną krótko warstwą wyjściową), których wyjście jest jednocześnie wyjściem z sieci. Pozostałe warstwy są nazywane ukrytymi, gdyż wyjścia neuronów w nich się znajdujących nie są „widoczne” na wyjściu sieci.

Dla neuronów wyjściowych można przyjąć, że funkcja aktywacji jest funkcją liniową.


Tak więc sieć neuronowa jest w swojej istocie pewnym wzorem, który da się przedstawić w formie graficznej. Wzór ten brzmi (w nieco nieformalnym zapisie):

yi=wijg(kwjkg(...(twstxt)))

Wielokrotne ważone sumowanie i przekształcanie funkcją aktywacji ma miejsce tyle razy, ile jest warstw neuronów w sieci, dlatego w powyższym wzorze pojawiają się trzy kropki. Jak więc widać, wyjście sieci neuronowej jest funkcją jej wejścia, przy czym funkcja ta jest parametryzowana zestawem parametrów 𝐰.

Interpretacja znaczenia parametrów sieci

Spróbujmy zrozumieć znaczenie poszczególnych parametrów, analizując przykładowe sieci zawierające jeden neuron wyjściowy z liniową funkcją aktywacji oraz jedną warstwę nieliniowych neuronów.

Na początek skoncentrujmy się na najprostszej sieci, mających jedno wejście i jedno wyjście (a zatem aproksymowana jest funkcja RR). Rozważmy sieć o dwóch neuronach ukrytych i jednym wyjściu liniowym. Opisuje ją wzór:

Parser nie mógł rozpoznać (błąd składni): {\displaystyle y=w_{10} + w_{11} g(w_{20} + w_{21} x) + w_{12} g(w_{30} + w_{31} x)\} ,

Znaczenie parametrów pojedynczego neuronu warstwy ukrytej jest następujące. Wartości proporcji Parser nie mógł rozpoznać (błąd składni): {\displaystyle w_{20}/w_{21}\} ,, Parser nie mógł rozpoznać (błąd składni): {\displaystyle w_{30}/w_{31}\} , służą do przesuwania wykresu funkcji Parser nie mógł rozpoznać (błąd składni): {\displaystyle g\} , wzdłuż osi OX. Z kolei parametry Parser nie mógł rozpoznać (błąd składni): {\displaystyle w_{21}, w_{31}\} , wpływają na „stromość” wykresu funkcji Parser nie mógł rozpoznać (błąd składni): {\displaystyle g(h_2)\} ,, Parser nie mógł rozpoznać (błąd składni): {\displaystyle g(h_3)\} ,. Tak więc, manipulując oboma parametrami, jesteśmy w stanie uzyskać funkcję o różnej „stromości”, różnie położoną na osi OX.

Parametry warstwy wyjściowej służą określeniu stopnia „wymieszania” wyjść neuronów ukrytych. Im większa wartość wagi Parser nie mógł rozpoznać (błąd składni): {\displaystyle w_{1i}\} ,, tym większy mnożnik użyty do wykresu wyjścia neuronu i-tego (dodajmy, że przyjęcie ujemnej wagi Parser nie mógł rozpoznać (błąd składni): {\displaystyle w_{1i}\} , powoduje odbicie wykresu funkcji wyjścia neuronu względem osi OX).

Z kolei w przypadku sieci o dwóch wejściach, aproksymującej funkcję R2R, z dwoma neuronami ukrytymi i jednym liniowym wyjściowym, mamy wzór:

Parser nie mógł rozpoznać (błąd składni): {\displaystyle y=w_{10} + w_{11} g(w_{20}+w_{21} x_1 + w_{22} x_2) + w_{12} g(w_{30} + w_{31} x_1 + w_{32} x_2)\} ,

Wagi Parser nie mógł rozpoznać (błąd składni): {\displaystyle w_{20}, w_{21}, w_{22}\} , odpowiadają za sposób ułożenia w przestrzeni powierzchni, odpowiadającej funkcji Parser nie mógł rozpoznać (błąd składni): {\displaystyle g(w_{20} + w_{21} x_1 + w_{22} x_2)\} , – powierzchnia ta ma poziomice ułożone wzdłuż prostej o równaniu Parser nie mógł rozpoznać (błąd składni): {\displaystyle w_{20} + w_{21} x_1 + w_{22} x_2 = 0\} ,, a w przekroju poprzecznym do poziomic ma kształt funkcji Parser nie mógł rozpoznać (błąd składni): {\displaystyle g\} ,. Zmieniając współczynniki wagowe, dokonujemy obrotów i przemieszczeń tej powierzchni, nie zmieniając jednak jej zasadniczego kształtu.

Wpływ wag perceptronu na jakość aproksymacji

Rzecz jasna wielce niepraktyczne byłoby dobieranie wartości współczynników wagowych sieci neuronowej „na oko” poprzez ręczne manipulacje tymi wartościami. Dlatego można zastosować to samo podejście, z którym mieliśmy już do czynienia w regresji liniowej i przeprowadzić regresję nieliniową.

Zasada jest podobna. Zakładamy, że sieć neuronowa ma jedno wyjście i wiele wejść. Oznaczymy aproksymowaną funkcję przez y(𝐱), natomiast wyjście sieci przez y^(𝐱). Dysponujemy pewnym zbiorem T={(𝐱,y(𝐱))}, zwanym zbiorem uczącym. Zdefiniujmy funkcję reszt r(𝐱)=y^(𝐱)y(𝐱). Zauważmy, że zarówno y^(𝐱), jak i r(𝐱) są funkcjami nie tylko wektora argumentów 𝐱, ale i wektora wag 𝐰.

Załóżmy, że miarą błędu jest błąd średniokwadratowy:

e(x)=r2(x)dx

W praktyce nie jesteśmy w stanie obliczyć powyższej całki, gdyż nie dysponujemy wartościami y(𝐱) dla dowolnej wartość argumentu, lecz tylko dla zbioru uczącego. Dlatego zamiast całką posługujemy się wartością błędu średniokwadratowego, obliczanego na zbiorze trenującym:

V(𝐰)=1nTTr2(x)

gdzie Parser nie mógł rozpoznać (błąd składni): {\displaystyle n_T\} , jest liczbą elementów zbioru trenującego. Jeśli nie będzie to budzić wątpliwości, wartość V(𝐰) będziemy dalej nazywać błędem sieci.

Teraz możemy już sformułować zadanie przeszukiwania, które leży u podstaw algorytmu uczenia sieci neuronowej. Przestrzeń przeszukiwań jest przestrzenią wektorów 𝐰, a zatem jest to przestrzeń kartezjańska Parser nie mógł rozpoznać (błąd składni): {\displaystyle R^{n_w}\} ,, gdzie Parser nie mógł rozpoznać (błąd składni): {\displaystyle n_w\} , jest liczbą wag sieci. W przykładach rozważanych powyżej wartość Parser nie mógł rozpoznać (błąd składni): {\displaystyle n_w\} , jest równa odpowiednio: siedem (dla sieci z jednym wejściem) i dziewięć (dla sieci z dwoma wejściami). W nieco bardziej realistycznych przypadkach wartość Parser nie mógł rozpoznać (błąd składni): {\displaystyle n_w\} , może się wahać od kilkudziesięciu do nawet kilkuset.

W przestrzeni przeszukiwań jest zdefiniowany funkcja, przyporządkowująca wektorowi wag 𝐰 wartość błędu V(𝐰). Przeszukiwanie ma na celu znalezienie takiego punktu (czyli wektora wag 𝐰), dla którego wartość błędu V(𝐰) jest minimalna – uczenie sieci jest więc zadaniem minimalizacji błędu w przestrzeni wag aproksymatora.

Powstaje pytanie – jak stwierdzić, że dla konkretnego wektora wag w przypada minimum błędu V(𝐰)? Jeśli wiemy, że jest dokładnie jedno takie minimum, a tak jest przy aproksymatorach liniowych, można obliczyć wartość gradientu błędu – będzie się on zerować w minimum. W przypadku aproksymatorów nieliniowych może być wiele minimów lokalnych, jednak zerowanie gradientu błędu jest warunkiem koniecznym istnienia minimum funkcji w punkcie.

Przystąpmy zatem do obliczenia gradientu. Dla neuronu wyjściowego (patrz rysunek) mamy:

Vwi=1nTT2e(𝐱)e(𝐱)wi=2nTTe(𝐱)y^(𝐱)wi

a ponieważ dla każdego neuronu:

yiwij=g(hi)hiwij=g(hi)yi

dostaniemy zatem:

Vwi=2nTTe(𝐱)g(h)yi

Dla neuronu warstwy ukrytej, którego wyjście jest dostarczane do neuronu wyjściowego (patrz rysunek), mamy:

Vwij=1nTT2e(𝐱)e(𝐱)wij=2nTTe(𝐱)e(𝐱)yiyiwij=2nTTe(𝐱)y^(𝐱)yiyiwij

a ponieważ dla każdego neuronu:

yiyj=g(hi)hiyj=g(hi)wij

dostaniemy zatem:

Vwij=2nTTe(𝐱)wig(h)g(hi)yi

Dla neuronu znajdującego się warstwę głębiej mamy:

Vwjk=1nTT2e(𝐱)e(𝐱)wjk=2nTTe(𝐱)e(𝐱)yijyiyjyjwjk=
=2nTTe(𝐱)wig(h)jwijg(hi)g(hj)yk

Oznaczmy:

δ(y^)=e(𝐱)g(h)
δ(yi)=wiδ(y^)g(h)
Parser nie mógł rozpoznać (błąd składni): {\displaystyle \delta(y_j) = \sum_j w_{ij} \delta{y_i} g'(h_j)\} ,

Możemy zatem zapisać:

Vwi=2nTTδ(y^)yi Vwi=2nTTδ(yi)yj Vwi=2nTTδ(yj)yk

O swoistej urodzie gradientu funkcji błędu względem wag sieci świadczy fakt, że do obliczenia gradientu względem wag warstwy ukrytej wystarczy informacja o gradiencie wag warstwy, która po niej następuje. Z tej przyczyny sposób obliczania gradientu jest nazywany algorytmem wstecznej propagacji błędu – błąd propaguje wzdłuż wag sieci w kierunku od wyjścia do wejścia.

Spełnienie warunku koniecznego istnienia minimum funkcji w punkcie nie daje oszacowania wartości funkcji błędu w tym punkcie. Powstaje zatem pytanie – czy dla dowolnej aproksymowanej funkcji istnieje sieć neuronowa, której błąd aproksymacji będzie nie większy niż pewien założony limit. Odpowiedzi na to pytanie udziela poniższe twierdzenie.

Twierdzenie o uniwersalnych właściwościach aproksymacyjnych

Załóżmy, że:

DRn jest zbiorem otwartym, skończonej miary
f:RnR jest funkcją ciągłą, taką że dla każdego 𝐞D|f(𝐱)|<
y^(𝐱) jest funkcją postaci:
y^(𝐱)=i=1..Ng(𝐰iT𝐱)
gdzie g(𝐱) jest funkcją ciągłą, ograniczoną i monotoniczną.

Wówczas:

dla każdego ϵ>0 istnieje taki zbiór wektorów wag {𝐰1,...,𝐰N}, że dla każdego xD

|y^(𝐱)f(𝐱)|<ϵ

Tak więc na mocy powyższego twierdzenia wiemy, że istnieje taka struktura sieci neuronowej, w której da się zrealizować aproksymację z dowolnie małym błędem. Analizując gradient, umiemy także stwierdzić, czy zestaw wag sieci jest minimum lokalnym funkcji błędu. Pozostaje rzecz najtrudniejsza – wyznaczyć taki zestaw wag, który jest nie tylko minimum lokalnym, lecz globalnym funkcji błędu.

Uczenie sieci

Jedną z najpowszechniej chyba stosowanych metod minimalizacji błędu sieci neuronowej jest przeszukiwanie przestrzeni wag według algorytmu wzrostu. Algorytm wychodzi z ustalonego arbitralnie wektora wag, po czym oblicza wektor gradientu funkcji błędu i jako nowy wektor wag przyjmuje wektor znajdujący się na półprostej wyprowadzonej w kierunku minus gradientu. Taką metodę nazywa się często metodą wstecznej propagacji błędu, a jej algorytm jest podany poniżej.

Algorytm uczenia perceptronu

Parser nie mógł rozpoznać (błąd składni): {\displaystyle t=0\} ,
inicjuj 𝐰(0)
powtarzaj
𝐰(t+1)=𝐰(t)ηV(𝐰(t))
Parser nie mógł rozpoznać (błąd składni): {\displaystyle t = t + 1\} ,
dopóki V(𝐰(t))>ϵ

Gradient funkcji błędu V(𝐰(t)) można obliczyć, korzystając z zasady wstecznej propagacji błędu, sumując iloczyny Parser nie mógł rozpoznać (błąd składni): {\displaystyle \delta(y_j)y_k\} , po wszystkich elementach zbioru trenującego Parser nie mógł rozpoznać (błąd składni): {\displaystyle T\} ,. Taki sposób uczenia nazywa się „trybem blokowym”. Istnieje jednak inna możliwość – zamiast wykorzystywać cały zbiór trenujący, można wziąć jedynie jego podzbiór TT (np. losowowybrany podzbiór, w szczególności podzbiór jednoelementowy), w każdej iteracji algorytmu uczenia wybierany na nowo, i obliczać gradient V(𝐰(t)) dla elementów zbioru Parser nie mógł rozpoznać (błąd składni): {\displaystyle T'\} ,. Jeśli ponumerujemy elementy zbioru Parser nie mógł rozpoznać (błąd składni): {\displaystyle T\} ,, a podzbiór Parser nie mógł rozpoznać (błąd składni): {\displaystyle T'\} , będzie jednoelementowy i będzie zawierać za każdym razem element z Parser nie mógł rozpoznać (błąd składni): {\displaystyle T\} , o kolejnym numerze porządkowym, to taki tryb uczenia nazywa się uczeniem sekwencyjnym.

Przyjęcie trybu blokowego powoduje, że możliwie najdokładniej przybliżana jest wartość gradientu funkcji błędu. Jeśli funkcja błędu obliczana dla zbioru Parser nie mógł rozpoznać (błąd składni): {\displaystyle T\} , ma „wąskie” minima lokalne – takie, których stwierdzenie nie jest możliwe po usunięciu choć jednego elementu zbioru Parser nie mógł rozpoznać (błąd składni): {\displaystyle T\} , – to przypuszczalnie nie odpowiadają one minimum globalnemu. Posłużenie się jedynie podzbiorem Parser nie mógł rozpoznać (błąd składni): {\displaystyle T'\} ,, w dodatku zmieniającym się z iteracji na iterację, pozwala algorytmowi skoncentrować się na „szerokich” minimach funkcji błędu, co czyni metodę uczenia nieco odporniejszą na minima lokalne. Wtedy jednak należy się liczyć z faktem, że warunek zatrzymania – spadek normy gradientu błędu poniżej założonego poziomu – może nie być spełniony. Można ten problem ominąć, zwiększając wartość Parser nie mógł rozpoznać (błąd składni): {\displaystyle \epsilon\} ,, albo ustalić apriori liczbę iteracji algorytmu.

Powyższy algorytm wykonuje krok w kierunku minus gradientu; szerokość tego kroku jest stałą krotnością gradientu (reguluje to współczynnik uczenia Parser nie mógł rozpoznać (błąd składni): {\displaystyle \eta\} ,). Takie postępowanie może prowadzić do bardzo długotrwałego procesu uczenia, jeśli gradient funkcji błędu ma małą „długość” (norma gradientu jest niewielką liczbą). Małe wartości gradientu z kolei występują w dwóch sytuacjach: gdy neurony są bliskie nasyceniu (tzn. wartości gradientu funkcji aktywacji Parser nie mógł rozpoznać (błąd składni): {\displaystyle g'(h_i)\} , są małe) albo gdy mamy do czynienia z minimum funkcji błędu (patrz poniższy rysunek).

W tym pierwszym przypadku, po wykonaniu ruchu w kierunku minus gradientu, znajdujemy się w nowym punkcie, w którym gradient ma najczęściej ten sam kierunek (a nierzadko i tę samą wartość) co poprzednio. Z tą sytuacją mamy do czynienia dla punktu Parser nie mógł rozpoznać (błąd składni): {\displaystyle w_1\} ,. W tym drugim przypadku, po wykonaniu podobnego kroku mamy najczęściej do czynienia ze zmianą kierunku i wartości gradientu (tak jest przykładowo dla punktu Parser nie mógł rozpoznać (błąd składni): {\displaystyle w_2\} ,). Obserwacje te stanowią motywację metody przyspieszenia algorytmu uczenia perceptronu, polegającej na dodaniu „momentu bezwładności”. Algorytm wygląda następująco:

Algorytm uczenia perceptronu z momentem bezwładności

inicjuj 𝐰(0)
Δ𝐰(0)=ηV(𝐰(0))
𝐰(1)=𝐰(0)Δ𝐰(0)
Parser nie mógł rozpoznać (błąd składni): {\displaystyle t = 1\} ,
powtarzaj
Δ𝐰(t)=ηV(𝐰(t))+αΔ𝐰(t1)
𝐰(t+1)=𝐰(t)Δ𝐰(t)
Parser nie mógł rozpoznać (błąd składni): {\displaystyle t = t + 1\} ,
dopóki V(𝐰(t))>ϵ

Współczynnik α jest momentem bezwładności i spełnia warunek Parser nie mógł rozpoznać (błąd składni): {\displaystyle 0 < \alpha < 1\} ,.

Jeśli zachodzi pierwszy z omawianych wcześniej przypadków (gradient w przybliżeniu niezmienny wzdłuż kierunku swoich zmian), wówczas efektywny współczynnik uczenia wynosi w przybliżeniu Parser nie mógł rozpoznać (błąd składni): {\displaystyle \eta (1 + \alpha)^k\} ,, gdzie Parser nie mógł rozpoznać (błąd składni): {\displaystyle k\} , jest liczbą kroków, w których gradient jest w przybliżeniu niezmienny. A zatem w kolejnych iteracjach algorytm uczenia wykonuje coraz większy krok, co pozwala przyspieszyć jego działanie. Z kolei gdy w kolejnych krokach gradient różni się, krok się zmniejsza. Przykładowo, dla V(𝐰(t))=V(𝐰(t1)), efektywny współczynnik uczenia wyniesie Parser nie mógł rozpoznać (błąd składni): {\displaystyle \eta (1 - \alpha)\} ,, co pozwala na dokładniejszą lokalizację minimum lokalnego funkcji błędu.

Omawiane algorytmy uczenia sieci korzystają z warunku koniecznego istnienia minimum funkcji w punkcie. Oznacza to, że wektor wag 𝐰(t) będzie zdążać do takiego wektora, dla którego przypada minimum lokalne funkcji błędu, niekoniecznie będące minimum globalnym. Nie jest znany algorytm dający gwarancję znalezienia minimum globalnego dowolnej funkcji w Parser nie mógł rozpoznać (błąd składni): {\displaystyle R^n\} ,, a zatem nie możemy liczyć na gwarancję wyznaczenia optymalnego wektora wag. Można jednak zwiększyć prawdopodobieństwo wyznaczenia minimum globalnego, stosując np. techniki omawiane we wcześniejszej lekcji poświęconej metodom przeszukiwania wykorzystującym losowość.