Neuronové sítě III.

První článek, druhý článek

V tomto článku se naučíme, co jsou to skryté vrstvy a aktivační funkce.

V prvním článku jsme si řekli, že neuronové sítě můžou vypadat třeba takto


Vstupů může být libovolné množství, výstupů může být libovolné množství, skrytých vrstev také a neuronů v nich také.

My jsme však zatím měli jen jeden vstup a jen jeden výstup. Podívejme se nyní na to, jak pracovat se skrytými vrstvami.

Mějme stejný příklad jako v předešlém článku, tedy chceme naučit síť násobit vstup trojkou a přičítat k němu dvojku. Tentokrát ale použijeme síť, která má jednu skrytou vrstvu, ve které jsou dva neurony. Takto jsme síť vytvořili jen pro ilustraci toho, jak se skrytou vrstvou počítat.

Mějme váhy \(w_1 = 0.1, w_2 = 0.2, w_3 = 0.3, w_4 = 0.4\) a bias ve skryté vrstvě \(b = 1\). Vstupy budeme mít \(i_1 = 3, i_2 = 5, i_3 = 10\) a k nim výstupy \(y_1 = 11, y_2 = 17, y_3 = 32\).

Označme si výstup neuronů ve skryté vrstvě \(h_1\) a \(h_2\) a výstup. Máme-li vstup \(i\), pak \(h_1\) bude \(h_1 = iw_1\) a obdobně \(h_2 = iw_2\).

Výstup pak spočteme jako \(a = w_3h_1+w_4h_2+b\).

Pro naše hodnoty tedy dostáváme \(h_1 = 3 \cdot 0.1 = 0.3\) a \(h_2 = 3 \cdot 0.2 = 0.6\). Spočtený výstup bude \(a = 0.3 \cdot 0.3 + 0.4 \cdot 0.6 + 1 = 1.33\).

Chybová funkce vyjde \(E = (a-y)^2 = (1.33-11)^2 = 93.5\). Nyní nás zajímá, jak se mění vůči jednotlivým váhám a vůči biasu. Budeme potřebovat následující vztahy:

\(\frac{\partial E}{\partial w_1} = \frac{\partial E}{\partial a} \frac{\partial a}{\partial h_1} \frac{\partial h_1}{\partial w_1} \)
\(\frac{\partial E}{\partial w_2} =\frac{\partial E}{\partial a} \frac{\partial a}{\partial h_2} \frac{\partial h_2}{\partial w_2} \)
\(\frac{\partial E}{\partial w_3} = \frac{\partial E}{\partial a} \frac{\partial a}{\partial w_3}\)
\(\frac{\partial E}{\partial w_4} = \frac{\partial E}{\partial a} \frac{\partial a}{\partial w_4}\)

\(\frac{\partial E}{\partial b} =\frac{\partial E }{\partial a} \frac{\partial a}{\partial b} \)

Všechny tyto výrazy zvládneme zderivovat:
\(\frac{\partial E}{\partial a} = 2(a-y)\)
\(\frac{\partial a}{\partial h_1} = w_3\)
\(\frac{\partial h_1}{\partial w_1} = i\)

\(\frac{\partial a}{\partial h_2} = w_4\)
\(\frac{\partial h_2}{\partial w_2} = i\)

\(\frac{\partial a}{\partial w_3} = h_1\)
\(\frac{\partial a}{\partial w_4} = h_2\)

\(\frac{\partial a}{\partial b} = 1\).

Všechny tyto výrazy vyčíslíme, aktualizujeme \(w_1, w_2, w_3, w_4\) a \(b\), opět spočteme výstup \(a\), opět vyčíslíme derivace, aktualizujeme… A opakujeme tak dlouho, dokud je naše chybová funkce větší, než bychom si přáli.

Lineární vs nelineární problémy

Zatím jsme zvládli řešit jen lineární problémy, tj. takové, jejichž výstup je na vstupu závislý lineárně. Byl-li výstup \(y\) a vstup \(i\), tak jsme hledali takovou váhu \(w\) a bias \(b\), aby platilo, že \(y = iw+b\). Tím jsme se ale omezili na relativně malou množinu možných problémů řešitelných neuronovou sítí. Grafem takové rovnice je vždy přímka.



Váha určuje sklon přímky a bias, jak moc vysoko či nízko je.

Mějme teď příklad, který nebude tak snadné řešit. Množina možných vstupů budou libovolná reálná čísla a po neuronové síti budeme chtít jen zjistit, zda je daný vstup větší či roven \(0\), či menší než \(0\).

Pokud dáme neuronové síti kladné číslo (či nulu), budeme chtít výstup \(1\), a pokud záporné, budeme chtít výstup \(0\).



Takto vypadá graf takové funkce, takže vidíme, že nejde o přímku. To znamená, že ať bychom vytvořili síť s libovolným počtem skrytých vrstev, bez aktivační funkce nebudeme mít nikdy síť, která počítá tak, jak bychom si přáli.

Mějme tento učící set \(i_1 = -2, y_1 = 0, \\ i_2 = -1, y_2 = 0, \\ i_3 = 0, y_3 = 1, \\ i_4 = 1, y_4 = 1,\\ i_5 = 2, y_5 = 1 \)

Pokusíme-li se spočítat váhu a bias se stejnou neuronovou sítí jako v minulém článku, tedy podle následujícího kódu

b = 4               #bias
w = 1               #váha

i_1 = -2             #první input
y_1 = 0            #output chtěný na první input

i_2 = -1            #druhý input
y_2 = 0            #output chtěný na druhý input

i_3 = 1             #třetí input
y_3 = 1            #output chtěný na třetí input

i_4 = 2
y_4 = 1

r = 0.01            #učící koeficient


def del_c_w(a, i, y):   #funkce na výpočet parciální derivace podle váhy
    return 2*(a-y)*i

def del_c_b(a,y ):      #funkce na výpočet parciální derivace podle biasu
    return 2*(a-y)

def a(i):               #funcke na výpočet toho, co nám vrací neuronová síť
    return w*i+b

def new_weight(w, i, y):                    #funkce na výpočet nové váhy
    return w - r*del_c_w(a(i),i, y)

def new_bias(b, i, y):                      #funkce na výpočet nového biasu
    return b - r*del_c_b(a(i), y)


for j in range(1,150):
    print("1->0")
    w, b = new_weight(w, i_1,y_1), new_bias(b, i_1, y_1)        #aktualizujeme váhu a bias podle prvního inputu a outputu
    print(w, b)

    print()

    print("2->0")
    w, b = new_weight(w, i_2, y_2), new_bias(b, i_2, y_2)   #aktualizujeme váhu a bias podle druhého inputu a outputu
    print(w, b)
    print()

    print("3->1")
    w, b = new_weight(w, i_3, y_3), new_bias(b, i_3, y_3)   #aktualizujeme váhu a bias podle třetího inputu a outputu
    print(w, b)
    print()

    print("4->1")
    w, b = new_weight(w, i_4, y_4), new_bias(b, i_4, y_4)  # aktualizujeme váhu a bias podle čtvrtého inputu a outputu
    print(w, b)
    print()

Nyní dostaneme váhu \(w = 0.3\) a bias \(b = 0.5\). Na input \(-2\) dostaneme \(-2 \cdot 0.3+0.5 = -0.1\), na input \(-1\) dostaneme \(-1 \cdot 0.3+0.5 = 0.2\), na input \(1\) dostaneme \(1 \cdot 0.3+0.5 = 0.8\) a konečně na input \(2\) dostaneme \(2 \cdot 0.21+0.102 = 1.1\). Vidíme, že na tyto vstupy máme výstupy opravdu relativně blízko tomu, co bychom si přáli.

Ale pokud bychom se podívali na výstup např. pro číslo \(10\), dostaneme \(10 \cdot 0.3+0.5 = 3.5\), což už je o dost více než jedna.

Pokus o vylepšení neuronové sítě

Budeme muset použít něco, čemu se říká aktivační funkce. To jsou různé nelineární funkce a pro různé případy se hodí různé z nich. My si pro ilustraci v tomto článku vezmeme tzv. logistickou funkci.

Mějme \(\sigma(x) = \frac{e^x}{1+e^x}\). Tato funkce může mít jako vstup libovolné reálné číslo, ale výstup je jen mezi \(0\) a jedničkou. Že je výstup vždy kladný je zřejmé z toho, že \(e^x\) nabývá pouze kladných hodnot, takže máme v čitateli i jmenovateli vždy kladné číslo.

Že je výstup vždy menší než \(1\) plyne z toho, že čitatel je vždy menší než jmenovatel.

Upravme si první tak trochu náhodný výraz, ale uvidíme, že se nám později bude hodit, totiž \(\sigma(x)(1-\sigma(x))\). Dostáváme \( \frac{e^x}{1+e^x} (1- \frac{e^x}{1+e^x}) = \frac{e^x}{1+e^x} \frac{1+e^x-e^x}{1+e^x} = \frac{e^x}{1+e^x} \frac{1}{1+e^x} = \frac{e^x}{(1+e^x)^2}\).

Nyní si zderivujme funkci \(\sigma(x)\). K tomu budeme potřebovat znát pravidlo pro derivaci podílu. To zní následovně:
Mějme funkce \(f(x), g(x)\). Potom \( (\frac{f(x)}{g(x)})‘ = \frac{f'(x)g(x)-f(x)g'(x)}{g^2(x)}\).

Pro naši funkci \(\sigma\) dostáváme \(\frac{\mathbb d \sigma(x)}{\mathbb d x} = \frac{e^x(1+e^x)-e^xe^x}{(1+e^x)^2} = \frac{e^x+e^xe^x-e^xe^x}{(1+e^x)^2} = \frac{e^x}{(1+e^x)^2}\).

Vidíme tedy, že pro naši funkci platí, že její derivace se rovná výrazu \(\sigma(x)(1-\sigma(x))\).

Použijeme neuronovou síť, která má nula skrytých vrstev a vypadá takto

Označme si \(x = iw+b, a(x) = \sigma(x) = \frac{e^x}{1+e^x}\). Použili jsme písmeno \(a\), abychom byli konzistentní s předešlými články, kde jsme označovali výstup neuronové sítě právě \(a\).

Stejně jako v minulých případech spočteme \(iw+b\). Rozdílem tentokrát je, že číslo, které nám vyjde, dosadíme do funkce \(a(x) = \sigma(x)\), což bude náš výstup. Tento krok nám zaručí, že budeme moct modelovat i nelineární systémy.

Chybová funkce bude \(E(a) = (a-y)^2\). Opět nás bude zajímat, jak se naše chybová funkce mění podle váhy a podle biasu, spočtěme si tedy\(\frac{\mathbb d E}{\mathbb d w}\) a \(\frac{\mathbb d E}{\mathbb db}\).

\(\frac{\mathbb d E}{\mathbb d w} = \frac{\mathbb d E}{\mathbb d a} \frac{\mathbb d a}{\mathbb d x} \frac{\mathbb d x}{\mathbb d w}\)

\(\frac{\mathbb d E}{\mathbb d b} = \frac{\mathbb d E}{\mathbb d a} \frac{\mathbb d a}{\mathbb d x} \frac{\mathbb d x}{\mathbb d b}\)

Spočteme si jednotlivé zlomky, dostáváme
\(\frac{\mathbb d E}{\mathbb d a} = 2(a-y)\)
\(\frac{\mathbb d a}{\mathbb d x} = a(1-a)\)
\(\frac{\mathbb d x}{\mathbb d w} = i\)
\(\frac{\mathbb d x}{\mathbb d b} =1\)

Dosadíme-li zpět do derivací chybové funkce, dostáváme

\(\frac{\mathbb d E}{\mathbb d w} = 2(a-y)a(1-a)i\)
\(\frac{\mathbb d E}{\mathbb d b} = 2(a-y)a(1-a)\).

A opět stejně jako v předešlých článcích aktualizujeme váhu a bias, tj. \(w_2 = w-r \frac{\mathbb d E}{\mathbb d w}\) a \(b_2 = b-r\frac{\mathbb d E}{\mathbb d b}\).

Zde python kód


from math import e

b = 1               #bias
w = 1               #váha

training_set = [(-2, 0), (-1, 0), (0, 1), (1, 1), (2, 1) ]      #data, na kterých budeme učit neuronovou síť
r = 0.01                                                        #učící koeficient

def sigma(x):                                                   #aktivační funkce sigma
    return e**x/(1+e**x)

def output(i,w,b):                                              #funkce, která na daný vstup vrátí výstup spočtený neuronovou sítí
    x = i*w+b
    return sigma(x)

def del_e_w(a, y, i):                                           #parciální derivace chybové funkce podle váhy
    return 2*(a-y)*a*(1-a)*i

def del_e_b(a,y):                                               #parciální derivace chybové funkce podle biasu
    return 2*(a-y)*a*(1-a)

def new_weight(de_dw):                                          #funkce na aktualizaci váhy
    return w-r*de_dw

def new_bias(de_db):                                            #funkce na aktualizaci biasu
    return b-r*de_db

for _ in range (100000):                                       #provedeme 100 000 iterací
    for train_pair in training_set:
        i = train_pair[0]                                       #vstup
        y = train_pair[1]                                       #na vstup požadovaný výstup
        a = output(i, w, b)                                     #výstup doopravdy spočtený naší neuronovou sítí

        de_dw = del_e_w(a, y, i)                                #parciální derivace chybové funkce podle váhy
        de_db = del_e_b(a, y)                                   #parciální derivace chybové funkce podle biasu

        w = new_weight(de_dw)                                   #aktualizace váhy
        b = new_bias(de_db)                                     #aktualizace biasu

Váha vyjde \(7.25\) a bias \(3.52\) a graf funkce \(\frac{e^{7.25x+3.52}}{1+e^{7.25x+3.52}} \) vypadá následovně

Vidíme, že záporná i kladná čísla to klasifikuje tak, jak má – až na čísla mezi \(-0.5\) a \(0\), které ohodnotí číslem větším než \(0.5\).

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *