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

3. diel - Neurónové siete - Krokovanie, bias a viac dimenzií

V minulej lekcii, Neurónové siete - Perceptron , sme spomenuli Perceptron a ukázali si niektoré druhy algoritmov vhodných na generovanie a triedenie dát.

V dnešnej lekcii kurzu Neurónovej siete - Krok za krokom si ukážeme krokovanie algoritmu, povieme si niečo o biase a lekciu zakončíme prípadom s 10-timi dimenziami.

Krokovanie algoritmu

Teraz, keď máme algoritmus, môžeme sa naň pozrieť bližšie. Pretože som vám sľúbil množstvo grafov, poďme si každú aktualizáciu vykresliť. Najprv vytvoríme dátovú sadu. Znížil som počet dátových bodov, aby bol príklad jasnejší:

data, classes = sklearn.datasets.make_blobs(
    n_samples=10,
    n_features=2,
    centers=[[-1,0.4],[1,0.6]],
    cluster_std=0.8,
    random_state=82
)
plt.scatter(data[:,0], data[:,1], c=classes)
plt.show()
graf1 - Neurónové siete - Pokročilé
Teraz si vykreslíme všetky zmeny. Na nižšie uvedených grafoch je červená priamka oddeľujúca nadrovinou a čierna priamka jej normála. Zle klasifikovaná inštancia (pri ktorej aktualizujeme váhy) je označená zeleno. Zelená priamka predstavuje inštančný vektor, tj vektor, ktorý chceme pripočítať (alebo odpočítať) od váh. Napokon, svetlo červené a modré priamky sú novou deliacou nadrovinou (po aktualizácii) a zodpovedajúce normála:
# Tréning perceptrónu
# Inicializácia váh
weights = np.random.RandomState(42).uniform(size=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)
        # aktualizácia váh
        weights_new = weights + (target - prediction) * instance

        # vykreslenie zmeny
        if (weights != weights_new).any():
            slope = - weights[0] / weights[1]  # vypočítanie sklonu
            plt.figure(figsize=(8, 5))
            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')  # vykreslenie deliacej nardoviny
            plt.plot([0, weights[0]],[0, weights[1]], c='black')  # vykreslenie normály
            plt.scatter(data[:,0], data[:,1], c=classes)  # vykreslenie dát
            plt.scatter(instance[0], instance[1], c='g')  # vykreslenie chybne klasifikovanej inštancie
            plt.plot([0, instance[0]], [0, instance[1]], c='g', alpha=0.4)  # vykreslenie inštančného vektora
            plt.plot(
                [weights[0], weights[0] + (target - prediction) * instance[0]],
                [weights[1], weights[1] + (target - prediction) * instance[1]],
                c='g', alpha=0.4)  # vykreslenie inštančného vektora z normály
            plt.plot(
                [0, weights[0] + (target - prediction) * instance[0]],
                [0, weights[1] + (target - prediction) * instance[1]],
                c='c', alpha=0.4)  # vykreslenie novej normály
            slope_new = - weights_new[0] / weights_new[1]  # výpočet sklonu novej priamky
            plt.plot(
                [data.min(axis=0)[0], data.max(0)[0]],
                [slope_new * data.min(axis=0)[0], slope_new * data.max(axis=0)[0]],
                c='r', alpha=0.2)  # vykreslenie deliacej nadroviny
            plt.axis('equal')
            plt.ylim(-2,4)
            plt.show()

        weights_changed = weights_changed or (weights != weights_new).any()
        weights = weights_new

# VYKRESLENIE KONEČNÝCH VÁH
slope = - weights[0] / weights[1]
plt.figure(figsize=(8, 5))
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()
Ako môžeme vidieť, po každej aktualizácii sa normála posunie bližšie (pozitívne dáta) alebo ďalej (negatívne dáta) od nesprávne klasifikovanej inštancie. To je hlavná myšlienka algoritmu učenia perceptrónu.

Bias

Zatiaľ sme nepoužili bias. Ak si pamätáte všeobecné rovnice priamky ?? + ?? + ? = 0, nastavili sme ? na nulu. Z tohto dôvodu prechádza priamka vždy súradnicou [0, 0]. Ale čo ak máme dáta, ktoré nemožno takto oddeliť? Radi by sme zaviedli posun ? (anglicky bias) a umožnili algoritmu posunúť deliacu nadrovinu z nulovej súradnice. Najprv si vygenerujeme novú dátovú sadu:

data, classes = sklearn.datasets.make_blobs(
    n_features=2,
    centers=2,
    random_state=44
)
plt.scatter(data[:,0], data[:,1], c=classes)
plt.show()
graf9 - Neurónové siete - Pokročilé
Túto dátovú sadu nemožno oddeliť pomocou nášho perceptrónového algoritmu, pretože ju nemôže oddeliť žiadna priamka prechádzajúca [0, 0]. Pre bias pridáme term ? do lineárnej kombinácie vstupov: ?=?(?? ? +?)

Bias sa učí rovnakým spôsobom ako váhy. Keď algoritmus nesprávne klasifikuje pozitívnu inštanciu, zvýši bias o 1. Keď je chybne klasifikovaná negatívna inštancia, zníži ho o 1:

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

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

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

# 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] - bias / weights[1], slope * data.max(axis=0)[0] - bias / weights[1]],
    c='r')
plt.show()
graf10 - Neurónové siete - Pokročilé
Ako vidíme, priamka sa posunula dole a už neprechádza cez [0, 0]. Niekedy je zbytočné riešiť bias samostatne. Môžeme ho zahrnúť do váh a doplniť dátové body jednotkami. Je to rovnaké, ako by sme presunuli dáta do novej dimenzie. Ukážme si to na jednorozmerných dátach. Máme pozitívne dáta {1, 2, 3} a negatívne dáta {4, 5, 6}. Radi by sme ich oddelili nadrovinou (to je v tomto prípade bodom), ktorá prechádza 0. To nie je možné, pretože oddeľujúcou nadrovinou môže byť iba bod 0:
data = np.array([[1],[2],[3],[4],[5],[6]])
classes = np.array([0,0,0,1,1,1])
plt.scatter(data, np.zeros((6,)), c=classes)
plt.scatter(0,0,c='r')
plt.show()
graf11 - Neurónové siete - Pokročilé
Ak by sme však presunuli dáta do novej dimenzie (tie by bolo v tomto prípade 2D), oddeľujúcou nadrovinou je priamka a dáta sú lineárne separované:
data = np.array([[1],[2],[3],[4],[5],[6]])
classes = np.array([0,0,0,1,1,1])
plt.scatter(data, np.ones((6,)), c=classes)
plt.scatter(0,0,c='r')
plt.plot([0, 6],[0, 1.75], c='r')
plt.show()
graf12 - Neurónové siete - Pokročilé
S biasom sa môžeme vysporiadať rovnakým spôsobom. Pridáme novú dimenziu s jednotkami av algoritme perceptróne použijeme 3 váhy:
# GENEROVANIE DÁT
data, classes = sklearn.datasets.make_blobs(
    n_features=2,
    centers=2,
    random_state=44
)
data = np.hstack([data, np.ones((data.shape[0],1))])

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

# 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]
bias = weights[2] / 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] - bias, slope * data.max(axis=0)[0] - bias],
    c='r')
plt.show()
graf13 - Neurónové siete - Pokročilé

Viacdimenzionálny prípad

Zatiaľ sme si ukázali iba dvojrozmerné prípady. Nie je nič, čo by bránilo použitiu viacerých dimenzionálnych dát, okrem toho, že ich nebudeme môcť vykresliť. Skúsme napríklad 10 dimenzií, ale bez vykresľovania, pretože vizualizáciu 10D dát by nebolo ľahké urobiť:

# GENEROVANIE DÁT
data, classes = sklearn.datasets.make_blobs(
    n_samples=1000,
    n_features=10,
    centers=2,
    random_state=42
)
data = np.hstack([data, np.ones((data.shape[0],1))])

train_data, test_data, train_classes, test_classes = sklearn.model_selection.train_test_split(data, classes, test_size=0.15)

# Tréning perceptrónu
weights = np.random.RandomState(42).uniform(-2, 2, 11)
old_weights = None
while (weights != old_weights).any():
    old_weights = weights
    for instance, target in zip(train_data, train_classes):
        prediction = sign(instance @ weights)
        weights = weights + (target - prediction) * instance

# TEST
predictions = [sign(instance @ weights) for instance in test_data]
print(f'Presnosť: {sklearn.metrics.accuracy_score(test_classes, predictions)}')
Výstup
Presnosť: 1.0

Ako vidíme, algoritmus dosiahol 100% presnosť. Správne oddelil dáta, pretože sú lineárne separované.

V budúcej lekcii, Neurónové siete - Normalizácia , sa zameriame na normalizáciu alebo štandardizáciu dátových bodov.


 

Predchádzajúci článok
Neurónové siete - Perceptron
Všetky články v sekcii
Neurónové siete - Pokročilé
Preskočiť článok
(neodporúčame)
Neurónové siete - Normalizácia
Č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