umz21/wyk/09_Sieci_neuronowe.ipynb
2021-03-03 08:05:12 +01:00

90 KiB
Raw Blame History

Uczenie maszynowe UMZ 2019/2020

12 maja 2020

9. Sieci neuronowe wprowadzenie

# Przydatne importy

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

%matplotlib inline

9.1. Perceptron

from IPython.display import YouTubeVideo
YouTubeVideo('cNxadbrN_aI', width=800, height=600)
Frank Rosenblatt
Frank Rosenblatt
perceptron

Pierwszy perceptron liniowy

  • Frank Rosenblatt, 1957
  • aparat fotograficzny podłączony do 400 fotokomórek (rozdzielczość obrazu: 20 x 20)
  • wagi potencjometry aktualizowane za pomocą silniczków

Uczenie perceptronu

Cykl uczenia perceptronu Rosenblatta:

  1. Sfotografuj planszę z kolejnym obiektem.
  2. Zaobserwuj, która lampka zapaliła się na wyjściu.
  3. Sprawdź, czy to jest właściwa lampka.
  4. Wyślij sygnał „nagrody” lub „kary”.

Funkcja aktywacji

Funkcja bipolarna:

g(z)={1gdy z>θ01wpp.

gdzie z=θ0x0++θnxn,
θ0 to próg aktywacji,
x0=1.

def bipolar_plot():
    matplotlib.rcParams.update({'font.size': 16})

    plt.figure(figsize=(8,5))
    x = [-1,-.23,1] 
    y = [-1, -1, 1]
    plt.ylim(-1.2,1.2)
    plt.xlim(-1.2,1.2)
    plt.plot([-2,2],[1,1], color='black', ls="dashed")
    plt.plot([-2,2],[-1,-1], color='black', ls="dashed")
    plt.step(x, y, lw=3)
    ax = plt.gca()
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    ax.xaxis.set_ticks_position('bottom')
    ax.spines['bottom'].set_position(('data',0))
    ax.yaxis.set_ticks_position('left')
    ax.spines['left'].set_position(('data',0))

    plt.annotate(r'$\theta_0$',
                 xy=(-.23,0), xycoords='data',
                 xytext=(-50, +50), textcoords='offset points', fontsize=26,
                 arrowprops=dict(arrowstyle="->"))

    plt.show()
bipolar_plot()

Perceptron schemat

Perceptron zasada działania

  1. Ustal wartości początkowe θ (wektor 0 lub liczby losowe blisko 0).
  2. Dla każdego przykładu (x(i),y(i)), dla i=1,,m
    • Oblicz wartość wyjścia o(i): o(i)=g(θTx(i))=g(nj=0θjx(i)j)
    • Wykonaj aktualizację wag (tzw. _perceptron rule): θ:=θ+Δθ Δθ=α(y(i)o(i))x(i)

θj:=θj+Δθj

Jeżeli przykład został sklasyfikowany poprawnie:

  • y(i)=1 oraz o(i)=1 : Δθj=α(11)x(i)j=0
  • y(i)=1 oraz o(i)=1 : Δθj=α(11)x(i)j=0

Czyli: jeżeli trafiłeś, to nic nie zmieniaj.

θj:=θj+Δθj

Jeżeli przykład został sklasyfikowany niepoprawnie:

  • y(i)=1 oraz o(i)=1 : Δθj=α(11)x(i)j=2αx(i)j
  • y(i)=1 oraz o(i)=1 : Δθj=α(11)x(i)j=2αx(i)j

Czyli: przesuń wagi w odpowiednią stronę.

Perceptron zalety i wady

Zalety:

  • intuicyjny i prosty
  • łatwy w implementacji
  • jeżeli dane można liniowo oddzielić, algorytm jest zbieżny w skończonym czasie

Wady:

  • jeżeli danych nie można oddzielić liniowo, algorytm nie jest zbieżny
def plot_perceptron():
    plt.figure(figsize=(12,3))

    plt.subplot(131)
    plt.ylim(-0.2,1.2)
    plt.xlim(-0.2,1.2)

    plt.title('AND')
    plt.plot([1,0,0], [0,1,0], 'ro', markersize=10)
    plt.plot([1], [1], 'go', markersize=10)

    ax = plt.gca()
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    ax.xaxis.set_ticks_position('none')
    ax.spines['bottom'].set_position(('data',0))
    ax.yaxis.set_ticks_position('none')
    ax.spines['left'].set_position(('data',0))

    plt.xticks(np.arange(0, 2, 1.0))
    plt.yticks(np.arange(0, 2, 1.0))


    plt.subplot(132)
    plt.ylim(-0.2,1.2)
    plt.xlim(-0.2,1.2)

    plt.plot([1,0,1], [0,1,1], 'go', markersize=10)
    plt.plot([0], [0], 'ro', markersize=10)

    ax = plt.gca()
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    ax.xaxis.set_ticks_position('none')
    ax.spines['bottom'].set_position(('data',0))
    ax.yaxis.set_ticks_position('none')
    ax.spines['left'].set_position(('data',0))

    plt.title('OR')
    plt.xticks(np.arange(0, 2, 1.0))
    plt.yticks(np.arange(0, 2, 1.0))


    plt.subplot(133)
    plt.ylim(-0.2,1.2)
    plt.xlim(-0.2,1.2)

    plt.title('XOR')
    plt.plot([1,0], [0,1], 'go', markersize=10)
    plt.plot([0,1], [0,1], 'ro', markersize=10)

    ax = plt.gca()
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    ax.xaxis.set_ticks_position('none')
    ax.spines['bottom'].set_position(('data',0))
    ax.yaxis.set_ticks_position('none')
    ax.spines['left'].set_position(('data',0))

    plt.xticks(np.arange(0, 2, 1.0))
    plt.yticks(np.arange(0, 2, 1.0))

    plt.show()
plot_perceptron()

Funkcje aktywacji

Zamiast funkcji bipolarnej możemy zastosować funkcję sigmoidalną jako funkcję aktywacji.

def plot_activation_functions():
    plt.figure(figsize=(16,7))
    plt.subplot(121)
    x = [-2,-.23,2] 
    y = [-1, -1, 1]
    plt.ylim(-1.2,1.2)
    plt.xlim(-2.2,2.2)
    plt.plot([-2,2],[1,1], color='black', ls="dashed")
    plt.plot([-2,2],[-1,-1], color='black', ls="dashed")
    plt.step(x, y, lw=3)
    ax = plt.gca()
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    ax.xaxis.set_ticks_position('bottom')
    ax.spines['bottom'].set_position(('data',0))
    ax.yaxis.set_ticks_position('left')
    ax.spines['left'].set_position(('data',0))

    plt.annotate(r'$\theta_0$',
                 xy=(-.23,0), xycoords='data',
                 xytext=(-50, +50), textcoords='offset points', fontsize=26,
                 arrowprops=dict(arrowstyle="->"))

    plt.subplot(122)
    x2 = np.linspace(-2,2,100)
    y2 = np.tanh(x2+ 0.23)
    plt.ylim(-1.2,1.2)
    plt.xlim(-2.2,2.2)
    plt.plot([-2,2],[1,1], color='black', ls="dashed")
    plt.plot([-2,2],[-1,-1], color='black', ls="dashed")
    plt.plot(x2, y2, lw=3)
    ax = plt.gca()
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    ax.xaxis.set_ticks_position('bottom')
    ax.spines['bottom'].set_position(('data',0))
    ax.yaxis.set_ticks_position('left')
    ax.spines['left'].set_position(('data',0))

    plt.annotate(r'$\theta_0$',
                 xy=(-.23,0), xycoords='data',
                 xytext=(-50, +50), textcoords='offset points', fontsize=26,
                 arrowprops=dict(arrowstyle="->"))

    plt.show()
plot_activation_functions()

Perceptron a regresja liniowa

Uczenie regresji liniowej:

  • Model: hθ(x)=ni=0θixi

  • Funkcja kosztu (błąd średniokwadratowy): J(θ)=1mmi=1(hθ(x(i))y(i))2

  • Po obliczeniu J(θ), zwykły SGD.

Perceptron a dwuklasowa regresja logistyczna

Uczenie dwuklasowej regresji logistycznej:

  • Model: hθ(x)=σ(ni=0θixi)=P(1|x,θ)

  • Funkcja kosztu (entropia krzyżowa): J(θ)=1mmi=1[y(i)logP(1|x(i),θ)+(1y(i))log(1P(1|x(i),θ))]

  • Po obliczeniu J(θ), zwykły SGD.

Perceptron a wieloklasowa regresja logistyczna

Wieloklasowa regresji logistyczna

  • Model (dla c klasyfikatorów binarnych): h(θ(1),,θ(c))(x)=softmax(ni=0θ(1)ixi,,ni=0θ(c)ixi)=[P(k|x,θ(1),,θ(c))]k=1,,c

  • Funkcja kosztu (przymując model regresji binarnej): J(θ(k))=1mmi=1[y(i)logP(k|x(i),θ(k))+(1y(i))logP(¬k|x(i),θ(k))]

  • Po obliczeniu J(θ), c-krotne uruchomienie SGD, zastosowanie softmax(X) do niezależnie uzyskanych klasyfikatorów binarnych.

  • Przyjmijmy: Θ=(θ(1),,θ(c))

hΘ(x)=[P(k|x,Θ)]k=1,,c

δ(x,y)={1gdy x=y0wpp.

  • Wieloklasowa funkcja kosztu J(Θ) (kategorialna entropia krzyżowa): J(Θ)=1mmi=1ck=1δ(y(i),k)logP(k|x(i),Θ)
  • Gradient J(Θ): J(Θ)Θj,k=1mmi=1(δ(y(i),k)P(k|x(i),Θ))x(i)j

  • Liczymy wszystkie wagi jednym uruchomieniem SGD

Podsumowanie

  • W przypadku jednowarstowej sieci neuronowej wystarczy znać gradient funkcji kosztu.
  • Wtedy liczymy tak samo jak w przypadku regresji liniowej, logistycznej, wieloklasowej logistycznej itp.
    • Wymienione modele to szczególne przypadki jednowarstwowych sieci neuronowych.
  • Regresja liniowa i binarna regresja logistyczna to jeden neuron.
  • Wieloklasowa regresja logistyczna to tyle neuronów ile klas.

Funkcja aktywacji i funkcja kosztu są dobierane do problemu.

9.2. Wielowarstwowe sieci neuronowe

czyli _Artificial Neural Networks (ANN) lub Multi-Layer Perceptrons (MLP)

Architektura sieci

  • Sieć neuronowa jako graf neuronów.
  • Organizacja sieci przez warstwy.
  • Najczęściej stosowane są sieci jednokierunkowe i gęste.
  • n-warstwowa sieć neuronowa ma n+1 warstw (nie liczymy wejścia).
  • Rozmiary sieci określane poprzez liczbę neuronów lub parametrów.

Sieć neuronowa jednokierunkowa (_feedforward)

  • Mając daną n-warstwową sieć neuronową oraz jej parametry Θ(1),,Θ(L) oraz β(1),,β(L) liczymy:

    a(l)=g(l)(a(l1)Θ(l)+β(l)).
  • Funkcje g(l) to tzw. funkcje aktywacji.
    Dla i=0 przyjmujemy a(0)=x (wektor wierszowy cech) oraz g(0)(x)=x (identyczność).
  • Parametry Θ to wagi na połączeniach miedzy neuronami dwóch warstw.
    Rozmiar macierzy Θ(l), czyli macierzy wag na połączeniach warstw a(l1) i a(l), to dim(a(l1))×dim(a(l)).
  • Parametry β zastępują tutaj dodawanie kolumny z jedynkami do macierzy cech.
    Macierz β(l) ma rozmiar równy liczbie neuronów w odpowiedniej warstwie, czyli 1×dim(a(l)).
  • Klasyfikacja: dla ostatniej warstwy L (o rozmiarze równym liczbie klas) przyjmuje się g(L)(x)=softmax(x).
  • Regresja: pojedynczy neuron wyjściowy jak na obrazku. Funkcją aktywacji może wtedy być np. funkcja identycznościowa.
  • Pozostałe funkcje aktywacji najcześciej mają postać sigmoidy, np. sigmoidalna, tangens hiperboliczny.
  • Mogą mieć też inny kształt, np. ReLU, leaky ReLU, maxout.

Uczenie wielowarstwowych sieci neuronowych

Mając algorytm SGD oraz gradienty wszystkich wag, moglibyśmy trenować każdą sieć.

  • Niech: Θ=(Θ(1),Θ(2),Θ(3),β(1),β(2),β(3))

  • Funkcja sieci neuronowej z grafiki:

hΘ(x)=tanh(tanh(tanh(xΘ(1)+β(1))Θ(2)+β(2))Θ(3)+β(3))

  • Funkcja kosztu dla regresji: J(Θ)=12mmi=1(hΘ(x(i))y(i))2

Jak obliczymy gradienty?

Θ(l)J(Θ)=?β(l)J(Θ)=?

  • Postać funkcji kosztu zależna od wybranej architektury sieci oraz funkcji aktywacji.

J(Θ)=12(a(L)y)2 a(L)J(Θ)=a(L)y

tanh(x)=1tanh2(x)