IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

2. diel - Neurónové siete - Perceptron

V minulej lekcii, Neurónové siete - Úvod , sme si načrtli vstupné požiadavky a menné konvencie pre kurz Neurónovej siete - Krok za krokom.

V dnešnej lekcii kurzu Neurónovej siete – Krok za krokom si najskôr povieme o fungovaní neurónu a vzápätí sa pustíme do tréningu perceptrónu a písania algoritmov.

Neurón

Najprv si ukážeme zjednodušený pohľad na neurón z biologického hľadiska:

Neurón - Neurónové siete - Pokročilé

Dendrity sú vstupy neurónu, na ktoré sa napájajú okolité neuróny. Vstupy sú potom odovzdané do tela neurónu (nucleus), ktorý môže byť excitovaný (aktivovaný) alebo nie. Keď je neurón excitovaný, jeho výstup prechádza axónom do ostatných neurónov. To je obrovské zjednodušenie, ale na naše účely to postačí.

Ako môžeme niečo také modelovať pomocou umelého neurónu? Potrebujeme vytvoriť funkciu, ktorá prijíma vstupy od ostatných neurónov (dendrity), spočíta excitáciu (nucleus) a výsledok pošle ďalej (axón). Pre zjednodušenie sú vstupy vážené a sčítané, výsledok je potom odovzdaný aktivačnú funkciu a jej výsledkom je výstup neurónu. To môžeme vidieť na nasledujúcom obrázku.

Umelý neurón - Neurónové siete - Pokročilé

(prah zatiaľ neberieme do úvahy)

Obrázok môžeme prepísať do matematického vzorca:

vzorec4 - Neurónové siete - Pokročilé

Než budeme pokračovať, zjednoťme si terminológiu. Vektory označím tučným textom (𝑥) a všetky jednorozmerné vektory budem považovať za riadkové vektory. Všetky vektorové násobenia budú bodové súčiny (ak nie je uvedené inak) - takže 𝑥𝑤 𝑇 je skalárny súčin a výsledok je skalár. Vektory budem indexovať od nuly (všimnite si, že obrázok používa číslo 1 ako prvý index).

Keďže je aktivačná funkcia väčšinou nastavená vopred, pri tréningu neurónu (alebo celej neurónovej siete), hľadáme váhy 𝑤, ktoré vedú k najlepšiemu riešeniu.

A to je všetko, čo zatiaľ potrebujeme vedieť. Teraz môžeme prejsť k prvému modelu – perceptrónu.

Perceptron

Perceptron je najjednoduchší model – používa funkciu sign() ako aktivačnú funkciu f.

Funkciu sign() definujeme nasledovne:

vzorec5 - Neurónové siete - Pokročilé

Vzorec definuje deliacu nadrovinu (tj nadrovinu, ktorá rozdeľuje priestor prvkov dát na dve polovice). Ak majú napríklad vstupné dáta dva rozmery, bude oddeľujúcou nadrovinou priamka, kde všetky dáta z prvej triedy budú nad priamkou, zatiaľ čo všetky dáta z druhej triedy skončia pod priamkou.

Ako už bolo naznačené v predchádzajúcom odseku, perceptrón môžeme použiť na klasifikačné úlohy, teda rozdelenie dát do dvoch tried. Je zaručené, že pre lineárne separovateľné dáta (dáta, ktoré je možné separovať) algoritmus učenia perceptrónu (pozri nižšie) vždy nájde nejakú nadrovinu deliacu obe triedy (viac v PDF od Shivaram Kalyanakrishnan).

Algoritmus učenia perceptrónu

Algoritmus učenia je veľmi jednoduchý. Váhy sú inicializované náhodne. Algoritmus hľadá inštanciu, ktorá je zle klasifikovaná. Ak je skutočná trieda inštancie kladná (a teda bola klasifikovaná ako negatívna), inštancia sa pridá k vektoru váhy. Ak je na druhej strane inštancia záporná (a bola klasifikovaná ako kladná), inštancia sa odpočíta od vektora váhy. Algoritmus končí, keď sú všetky inštancie správne klasifikované.

Pre klasifikáciu použijeme náhodné dáta generované sklearnou knižnicou:

# Načítanie knižníc
import sklearn.datasets
import sklearn.model_selection
import sklearn.metrics
import matplotlib.pyplot as plt
import numpy as np

# Definovanie funkcie sign
def sign(x):
    return 0 if x < 0 else 1

# Generovanie dát
data, classes = sklearn.datasets.make_blobs(n_samples=100, n_features=2, centers=2, random_state=42)

# Vykreslenie dát
plt.scatter(data[:,0], data[:,1], c=classes)
plt.show()
graf1 - Neurónové siete - Pokročilé
Toto sú údaje, ktoré sa snažíme klasifikovať. Poďme si teraz napísať algoritmus učenia perceptrónu:
# Inicializácia váh
weights = np.random.RandomState(42).uniform(-2, 2, 2)

# Opakovanie až do konvergencie
weights_changed = True
while weights_changed:
    weights_changed = False

    # pre každý výskyt v dátach
    for instance, target in zip(data, classes):
    # predpoveď výstupu perceptrónu
        prediction = sign(instance @ weights)
        if prediction == target:
            # správna klasifikácia
            continue
        elif target == 1:
            # pozitívna klasifikácia ako negatívna - pridanie inštancie k váham
            weights = weights + instance
        elif target == 0:
            # negatívna klasifikácia ako pozitívna - odčítanie inštancie od váh
            weights = weights - instance
        weights_changed = True

Ako som povedal, perceptrón definuje deliacu nadrovinu. Nadrovina je definovaná váhami perceptrónu a pretože sme v 2D, oddeľujúcou nadrovinou je priamka. Možno si pamätáte vzorec priamky v normálnom tvare 𝛼𝑥+𝛽𝑦+𝛾=0. V našom prípade je 𝑤 normála priamky a teda 𝛼=𝑤 0, 𝛽=𝑤 1 a 𝛾=0 (normála je kolmá na priamku). Poďme teraz nájdenú deliacu nadrovinu vykresliť:

# Výpočet sklonu priamky
slope = - weights[0] / weights[1]

# Vykreslenie dát
plt.scatter(data[:,0], data[:,1], c=classes)

# Vykreslenie oddeľovacej priamky
plt.plot(
    [data.min(axis=0)[0], data.max(0)[0]],
    [slope * data.min(axis=0)[0], slope * data.max(axis=0)[0]],
    c='r')
plt.show()
graf2 - Neurónové siete - Pokročilé
Ako môžeme vidieť, priamka je medzi týmito dvoma triedami, ale je o niečo bližšie k žltej triede. Pravdepodobne by sme chceli mať hranicu presne medzi triedami. Pretože sú ale všetky body už klasifikované správne, algoritmus nemôže priamku nijako upraviť. To je všeobecne nevýhoda perceptrónu. Nájde deliacu nadrovinu, ale nemusí to byť tá najlepšia. Skúsme zmeniť dáta a znova perceptrón potrénovať:
# GENEROVANIE DÁT
data, classes = sklearn.datasets.make_blobs(n_samples=100, n_features=2, centers=2, random_state=48)

# Tréning perceptrónu
# Inicializácia váh
weights = np.random.RandomState(42).uniform(-2, 2, 2)

# Opakovanie až do konvergencie
weights_changed = True
while weights_changed:
    weights_changed = False

    # pre každú inštanciu v dátach
    for instance, target in zip(data, classes):
        # predikcia výstupu perceptrónu
        prediction = sign(instance @ weights)
        if prediction == target:
            # správne zaradenie
            continue
        elif target == 1:
            # pozitívne klasifikované ako negatívne - pridanie inštancie k váham
            weights = weights + instance
        elif target == 0:
            # negatívne klasifikované ako pozitívne - odčítanie inštancie od váh
            weights = weights - instance
        weights_changed = True

# VYKRESLENIE
# Vypočítanie sklonu priamky
slope = - weights[0] / weights[1]

# Vykreslenie dát
plt.scatter(data[:,0], data[:,1], c=classes)

# Vykreslenie oddeľovacej priamky
plt.plot(
    [data.min(axis=0)[0], data.max(0)[0]],
    [slope * data.min(axis=0)[0], slope * data.max(axis=0)[0]],
    c='r')
plt.show()
graf3 - Neurónové siete - Pokročilé
Ako môžeme vidieť, všetky body sú správne klasifikované, ale deliaca nadrovina je presne vedľa žltých bodov. To rozhodne nie je priamka, ktorú sme chceli získať! Algoritmus sa môžeme pokúsiť spustiť viackrát s rôznou inicializáciou váh a vybrať najlepší výsledok, ktorý môžeme získať. Môžeme tiež iterovať dáta nie sekvenčne, ale náhodne. Ďalšie prístupy (ktoré nevyžadujú zapojenie programátora) preberiem neskôr.

Zjednodušenie pravidla aktualizácie

Pravidlo aktualizácie môžeme trochu zjednodušiť pomocou vzorca 𝑤 = 𝑤 + (target - prediction) ∗ 𝑥. Keď je predpoveď správna, rozdiel target - prediction je 0 a nevykonáva sa žiadna aktualizácia. Keď je 𝑡𝑎𝑟𝑔𝑒𝑡 = 0 a 𝑝𝑟𝑒𝑑𝑖𝑐𝑡𝑖𝑜𝑛=1 (negatívna inštancia je klasifikovaná kladne), inštancia sa odpočíta. V prípade 𝑡𝑎𝑟𝑔𝑒𝑡 = 1 a 𝑝𝑟𝑒𝑑𝑖𝑐𝑡𝑖𝑜𝑛 = 0 (pozitívna inštancia klasifikovaná ako negatívna), inštancia sa prište k váham. Môžeme tak celú aktualizáciu prepísať na jeden riadok.

Nakoniec sa ešte musíme uistiť, že sledujeme zmeny váh w. Nové váhy môžeme porovnať s váhami z predchádzajúcej iterácie a pokiaľ neboli vykonané žiadne zmeny, môže algoritmus ukončiť:

# GENEROVANIE DÁT
data, classes = sklearn.datasets.make_blobs(n_samples=100, n_features=2, centers=2, random_state=42)

# Tréning perceptrónu
# Inicializácia váh
weights = np.random.RandomState(42).uniform(-2, 2, 2)

# Opakovanie až do konvergencie
old_weights = None
while (weights != old_weights).any():
    old_weights = weights

    # pre každú inštanciu v dátach
    for instance, target in zip(data, classes):
        # predikcia výstupu perceptrónu
        prediction = sign(instance @ weights)
        # aktualizácia váh
        weights = weights + (target - prediction) * instance

# VYKRESLENIE
slope = - weights[0] / weights[1]
plt.scatter(data[:,0], data[:,1], c=classes)
plt.plot(
    [data.min(axis=0)[0], data.max(0)[0]],
    [slope * data.min(axis=0)[0], slope * data.max(axis=0)[0]],
    c='r')
plt.show()
graf4 - Neurónové siete - Pokročilé
Základy máme úspešne za sebou. Nabudúce pôjdeme viac do detailu 🙂

V budúcej lekcii, Neurónové siete - Krokovanie, bias a viac dimenzií , si ukážeme krokovanie, bias a prípad s 10-timi dimenziami.


 

Predchádzajúci článok
Neurónové siete - Úvod
Všetky články v sekcii
Neurónové siete - Pokročilé
Preskočiť článok
(neodporúčame)
Neurónové siete - Krokovanie, bias a viac dimenzií
Článok pre vás napísal Patrik Valkovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity