90 KiB
# Przydatne importy
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
from IPython.display import YouTubeVideo
YouTubeVideo('cNxadbrN_aI', width=800, height=600)



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:
- Sfotografuj planszę z kolejnym obiektem.
- Zaobserwuj, która lampka zapaliła się na wyjściu.
- Sprawdź, czy to jest właściwa lampka.
- Wyślij sygnał „nagrody” lub „kary”.
Funkcja aktywacji
Funkcja bipolarna:
g(z)={1gdy z>θ0−1wpp.
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 – zasada działania
- Ustal wartości początkowe θ (wektor 0 lub liczby losowe blisko 0).
- 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(n∑j=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=α(1−1)x(i)j=0
- y(i)=−1 oraz o(i)=−1 : Δθj=α(−1−−1)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=α(1−−1)x(i)j=2αx(i)j
- y(i)=−1 oraz o(i)=1 : Δθj=α(−1−1)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()
Uczenie regresji liniowej:
Model: hθ(x)=n∑i=0θixi
Funkcja kosztu (błąd średniokwadratowy): J(θ)=1mm∑i=1(hθ(x(i))−y(i))2
Po obliczeniu ∇J(θ), zwykły SGD.
Uczenie dwuklasowej regresji logistycznej:
Model: hθ(x)=σ(n∑i=0θixi)=P(1|x,θ)
Funkcja kosztu (entropia krzyżowa): J(θ)=−1mm∑i=1[y(i)logP(1|x(i),θ)+(1−y(i))log(1−P(1|x(i),θ))]
Po obliczeniu ∇J(θ), zwykły SGD.
Wieloklasowa regresji logistyczna
Model (dla c klasyfikatorów binarnych): h(θ(1),…,θ(c))(x)=softmax(n∑i=0θ(1)ixi,…,n∑i=0θ(c)ixi)=[P(k|x,θ(1),…,θ(c))]k=1,…,c
Funkcja kosztu (przymując model regresji binarnej): J(θ(k))=−1mm∑i=1[y(i)logP(k|x(i),θ(k))+(1−y(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(Θ)=−1mm∑i=1c∑k=1δ(y(i),k)logP(k|x(i),Θ)
Gradient ∇J(Θ): ∂J(Θ)∂Θj,k=−1mm∑i=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(l−1)Θ(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(l−1) i a(l), to dim(a(l−1))×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.
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(Θ)=12mm∑i=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)=1−tanh2(x)