Neuronové sítě I.



Neuronové sítě jsou posledních pár let hodně velké téma. V několika následujících článcích si postupně vytvoříme neuronové sítě od nejmenších po největší a nakonec si ukážeme, jak bychom je programovali v Pythonu.

Nejprve si ale musíme říct něco o matematice, kterou budeme používat. Budeme potřebovat znát funkce, derivace a parciální derivace.

Funkce

Funkce je určitý předpis, který každé hodnotě z našeho definičního oboru přiřadí jinou hodnotu. Např. definičním oborem můžou být všechna reálná čísla a funkce každému číslu přiřadí dané číslo na druhou. Tedy např. \(f(3) = 3^2 = 9\), obecněji \(f(x) = x^2\).

Pokud máme dvě funkce, můžeme je spolu skládat. Mějme funkce \(f(x) = \sin x\) a \(g(x) = x^2\). Potom funkcí \( (f \circ g)(x)\) rozumíme \(f(g(x))\) neboli spočteme hodnotu funkce \(g\) v bodě \(x\) a tuto spočtenou hodnotu dosadíme do předpisu funkce \(f\). V tomto případě tedy např. \(f(g(10)) = f(10^2) = f(100) = \sin 100\). Předpis funkce v tomto případě je \(f(g(x)) = \sin(x^2)\).

Funkci \(f\) nazveme vnější funkcí a funkci \(g\) nazveme vnitřní funkcí.

Derivace funkce

Derivace funkce je nástroj ke zjištění, jak rychle funkce v daném bodě klesá, či roste. Její geometrický význam je, že nám říká, jaký je sklon tečny v daném bodě. Pokud jsme tedy v bodě, kde funkce roste, je sklon tečny kladný. Pokud v bodě, kde klesá, je sklon tečny záporný. V bodě, kde neroste, ani neklesá, je sklon nulový.

Vzhledem k tomu, že derivace popisuje, jak rychle funkce roste či klesá, můžeme si ji představovat tak, že nám říká, jak moc se změní výstup funkce, když trochu změníme (trochu zmenšíme či trochu zvětšíme) vstup.

Máme-li graf funkce \(x^2\), vidíme, že od \(-\infty\) do \(0\) klesá a od \(0\) do \(\infty\) roste. V bodě \(0\) má minimum.

Podíváme-li se na tečny v nějakých bodech, kde funkce roste, tak jejich sklon bude kladný (tj. bude to rostoucí přímka).

V bodě \(1\) je funkce rostoucí a sklon tečny kladný

Podíváme-li se na tečnu v nějakém bodě, v kterém funkce klesá, její sklon bude záporný (tj. dostaneme klesající přímku).

V bodě \(-1\) je funkce klesající a sklon tečny záporný

Nakonec v bodě, kde funkce neroste ani neklesá, bude sklon tečny nulový, tj. dostaneme rovnoběžku s osou \(x\) (v tomto případě bude tečna přímo ležet na ose \(x\)).

V bodě \(0\) funkce neroste ani neklesá, sklon tečny je nulový


S pojmem derivace úzce souvisí pojmy lokální minimum a lokální maximum. Je-li bod minimem, všechny body v jeho nějakém malém okolí mají vyšší hodnotu. Pro maximum přesně naopak, tedy všechny body v jeho nějakém malém okolí mají nižší hodnotu.

V bodech maxima a minima funkce neroste ani neklesá, derivace v tomto bodě je nulová. Pozor, opačně to ale neplatí, viz funkci \(f(x) = x^3\). Jde o tzv. sedlový bod, ale takovýmito příklady se zde nebudeme zabývat.

Několik pravidel o derivování

Mohli bychom zde napsat formální definici derivace a odvodit z toho nějaká pravidla o derivování, ale zde se omezíme jen na postulování několika pravidel o derivování, protože zatím jich víc potřebovat nebudeme. Derivaci funkce \(f\) budeme značit buď \( (f(x))’\) anebo \(\frac{\mathbb d f}{\mathbb d x}\).

Je-li \(c\) nějaké reálné číslo, pak \( c’ = 0\).
Je-li \(c\) nějaké reálné číslo, pak \( (cf(x))’ = cf'(x)\).
Je-li \(n\) nějaké přirozené číslo, pak \( (x^n)’ = nx^{n-1}\).
\( (f+g)’ = f’+g’\).
\( (\sin(x))’ = \cos(x)\).

Derivace složené funkce

Jednou z nejdůležitějších věcí při počítání s neuronovými sítěmi je tzv. řetězové pravidlo (anglicky chain rule), které nám říká, jak zderivovat složenou funkci. Mějme opět \(f(x) = \sin(x), g(x) = x^2\), tedy \(f(g(x)) = \sin (x^2)\). Víme, že derivace sinu je kosinus a že derivací \(x^2\) je \(2x\).

Chain rule zní takto: \( (f(g(x)))’ = f'(g(x)) \cdot g'(x)\). Neboli zderivujeme funkci \(f\) a do argumentu výsledné funkce dosadíme funkci \(g(x)\) a toto celé pak vynásobíme derivací funkce \(g\). V našem případě je \(f'(x) = \cos(x)\), tedy \(f'(g(x)) = \cos(x^2)\). Dále máme \(g'(x) = 2x\). Dohromady tedy \( (f(g(x)))’ = (\sin (x^2))’ = cos(x^2)\cdot 2x\).

Pokud použijeme značení, kde derivace připomíná zlomek (ale NENÍ jím!), tak chain rule má tuto podobu: \(\frac{\mathbb d f}{\mathbb d x} = \frac{\mathbb d f}{\mathbb d g} \frac{\mathbb d g}{\mathbb d x}\).

NEURONOVÁ SÍŤ

Nyní se konečně dostáváme k samotným neuronovým sítím. Neuron je něco, co má vstup, na který reaguje a pošle dál nějaký výstup. Obecně vypadá neuronová síť nějak takto

Zeleně jsou naše vstupy, červeně tzv. skryté vrstvy a modře výstup. Všechny neurony jsou spojeny se všemi v další vrstvě. Vstupů můžeme mít libovolné množství, skrytých vrstev také (stejně jako může být jakýkoliv počet neuronů v nich) a výstupů můžeme mít také libovolné množství.

Pro názornost zatím mějme neuronovou síť, kde je nula skrytých vrstev, jen jeden vstup a jen jeden výstup

Na vstupu bude číslo \(1.5\) a neuronovou síť chceme naučit, že na tento vstup nám má dát výstup \(0.5\), tedy výstup je třikrát menší než vstup. Vstup si označme \(i\) a výstup, který na tento vstup chceme, označme \(y\). Mezi každými dvěma neurony je tzv. váha. Váha je číslo, kterým se násobí výstup z neuronu

Váhu získáme tak, že ji náhodně vygenerujeme. Mějme např. váhu \(w = 1.1\). Tedy nyní nám na číslo \(1.5\) neuronová síť vrátí \(i \cdot w = 1.5 \cdot 1.1 = 1.65\), což je o hodně jiné číslo než \(0.5\). Označme si jej \(a\), tedy \(a = i \cdot w\). Musíme nějak kvantifikovat chybu a pak se ji pokusit nějak snížit.

K tomu nám slouží tzv. chybová funkce. Pokud se podíváme na výstup, který bychom očekávali, a odečteme výstup, který jsme dostali, kvantifikujeme tím, jak velká je naše chyba. Abychom neměli problém s tím, že někdy dostaneme kladné a někdy záporné číslo, tak vždy výsledek umocníme na druhou.

Označme tuto funkci \(C\) a definujme jako \(C(a) = (a-y)^2\). Vidíme tedy, že naše chybová funkce je kvadratickou funkcí. Našim přáním je, aby tato funkce vyšla nula (či alespoň co nejblíž nule). Tj. hledáme bod, kdy derivace této funkce je co nejblíž nule – protože pak budeme v minimu. Obecněji v maximu či sedlovém bodě, ale my víme, že kvadratická funkce nemá ani maximum, ani sedlový bod.

Při výstupu \(1.65\) dostáváme, že \(C(1.65) = (1.65-0.5)^2\), tedy \(C(1.65) = 1.3225\). Toto číslo se pokusíme co nejvíc zminimalizovat. Zajímá nás, jak moc se změní \(C\), pokud trochu změníme naši váhu \(w\). K zjištění přesně tohoto slouží derivace, tj. nás zajímá \(\frac{\mathbb d C}{\mathbb d w}\). Povšimněme si, že \(a\) je funkcí \(w\), tedy můžeme psát \(a = a(w)\). Z toho potom plyne, že \(C = C(a(w))\), takže derivaci podle \(w\) budeme muset spočítat podle chain rule.

Dostáváme \(\frac{\mathbb d C}{\mathbb d w} = \frac{\mathbb d C}{\mathbb d a} \cdot \frac{\mathbb d a}{\mathbb d w}\). V našem případě \(\frac{\mathbb d C}{\mathbb d a} = 2(a-y)\) a \(\frac{\mathbb d a}{\mathbb d w} = i\). Dohromady \(\frac{\mathbb d C}{\mathbb d w} = 2i(a-y)\).
Pro váhu \(w = 1.1\) dostáváme \(\frac{\mathbb d C}{\mathbb d w} = 2 \cdot 1.5 \cdot (1.65-0.5) = 3.45\).

Z toho, že je derivace kladná, vidíme , že váha je moc velká. Musíme ji tedy zmenšit. My tušíme, že chceme váhu dostat na \(0.33\). Nemůžeme tedy prostě vzít naši váhu a odečíst od ní \(3.45\). Odečteme však toto číslo přenásobené tzv. učícím koeficientem \(r\), tedy nová váha \(w_1 = w – r \cdot \frac{\mathbb d C}{\mathbb d w}\).

Mějme např. \(r = 0.1\), potom nová váha bude \(w_1 = 1.1-0.1 \cdot 3.45 = 0.755\).

S touto novou váhou opět spočteme náš output, podíváme se na derivaci chybové funkce, spočteme novou váhu atd., dokud nebudeme dostatečně spokojeni s tím, jak velkou chybu máme. No a máme hotovo.

(Ne příliš promyšlený) kód v Pythonu, který dělá přesně to, co jsme zde popsali

"""jeden vstup, jeden výstup, nula skrytých vrstev"""

i = 1.5                 #input
y = 0.5                 #ouput
w = 1.1                 #náhodně vybraná váha weight
r = 0.1                 #učící koeficient

a = i*w                 #output spočítaný naší neuronovou sítí

def err(a):             #funkce počítající, jak velký je náš error
    return (a-y)**2

def der(a):             #funkce počítající derivaci error funkce v bodě a
    return 2*(a-y)*i


while err(a) >= 10**(-5):       #dokud bude chyba velká, snižujme naši váhu
    w = w-r*der(a)
    a = i*w

print(w)                #naše výsledná váha

A příště přidáme i skryté vrstvy!

2 komentáře u „Neuronové sítě I.“

Napsat komentář

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