Spis treści 1 Wstęp 1.1 Importy 2 Zbiór uczący 3 Klasyfikacja 3.1 Rysunki dodatkowe 4 Polecenia dodatkowe Wstęp Celem tych ćwiczeń jest zapoznanie się z klasyfikacją za pomocą sieci neuronowych. Importy # -*- coding: utf-8 -*- import matplotlib %matplotlib notebook #matplotlib.use('tkagg') import numpy as np import pylab as py from scipy import diag, arange, meshgrid, where from numpy.random import multivariate_normal Zbiór uczący Funkcja generująca zbiór uczący: def gen(ile): mu = [(-1,),(2,4),(3,1)] cov = [diag([1,1]), diag([0.5,1.2]), diag([1.5,0.7])] X = np.zeros((ile*3, 2)) # miejsce na dane wejściowe Y = np.zeros((ile*3,1),dtype = int) # miejsce na dane wyjściowe for klasa in range(3): X[klasa*ile:(klasa+1)*ile] = multivariate_normal(mu[klasa],cov[klasa],ile) Y[klasa*ile:(klasa+1)*ile] = klasa return (X,Y) Problem klasyfikacji jest zazwyczaj łatwiejszy do rozwiązania jeśli w warstwie wyjściowej umieścimy
tyle neuronów ile jest klas i docelowe klasy są kodowane jako wysoki stan jednego z neuronów wyjściowych. Nawiązuje to trochę do rozwiązań jakie wyprowadziliśmy na wykładzie dla regresji wielorakiej (softmax). def convert_to_target(y): target = np.zeros((len(y), len(np.unique(y)))) for i in range(len(y)): target[i,y[i,]] = 1 return target Testujemy: X,Y = gen(5) print(y) py.scatter(x[:,],x[:,1],c = Y,s = 10) target = convert_to_target(y) print(target) Klasyfikacja W tym przykładzie pokażemy jak przy pomocy sieci neuronowej zbudować klasyfikator. Zadanie będzie polegało na zaliczaniu punktów do jednego z trzech typów. Dane będą pochodzić z trzech rozkładów normalnych dwuwymiarowych o różnych parametrach. Przygotujemy zestawy danych. Tablica mu zawiera wektory średnich dla każdego z trzech rozkładów, tablica cov zawiera macierze kowariancji dla tych rozkładów: N_przykladow =37 X, Y = gen(n_przykladow) # przykłady do ciągu uczącego X_m, Y_m = gen(n_przykladow) # przykłady do ciągu monitorującego py.scatter(x[:,],x[:,1],c = Y,s = 10) py.scatter(x_m[:,],x_m[:,1],c = Y_m, s = 10, marker = 's') Warto przekodować te dane tak aby jedna klasa była reprezentowana przez jeden neuron wyjściowy: target = convert_to_target(y) target_m = convert_to_target(y_m) Teraz wytworzymy sieć. Rozmiar wejścia i wyjścia muszą się zgadzać z rozmiarami danych wejściowych i wyjściowych, odpowiednio.
class siec(object): def init (self, X, Y, N_hid=3): self.x = X self.y = Y self.n_wej = X.shape[1] self.n_wyj = Y.shape[1] self.n_hid = N_hid # inicjujemy połączenia # wagi ułożone są tak, że w kolejnych wierszach są kolejne neurony # a w kolumnach wagi od konkretnego neuronu # to +1 jest wagą dla obciążenia self.w_1 = (2*np.random.random((self.N_hid, self.n_wej+1)) - 1)/self.N_wej # pomiędzy warstwą pierwszą (wejściem) a warstwą ukrytą self.w_2 = (2*np.random.random((self.N_wyj, self.n_hid+1)) - 1)/self.N_hid self.dw1 = np.zeros((self.n_hid, self.n_wej+1)) self.dw2 = np.zeros((self.n_wyj, self.n_hid+1)) def g1(self, x): f = np.exp(-x) f[f>1e8]=1e8 y = 1./(1+f) def g1_prim(self, x): y = x*(1-x) def g2(self, x): y = x def g2_prim(self, x): y = 1 def get_params(self): return np.concatenate((self.w_1.reshape(-1), self.w_2.reshape(-1))) def predict(self, x): # propagacja "w przód" self.a_0 = np.vstack((1,x)) # z warstwy wejściowej (zerowej) wychodzi a_0 z_1 = np.dot( self.w_1, self.a_0 )# na warstwe 1 wchodzą iloczyny skalarne self.a_1 = np.vstack((1,self.g1(z_1))) # dokładamy 1 i dostaję wyjście z warstwy 1 z_2 = np.dot( self.w_2, self.a_1 ) # na warstwe 3 wchodzą iloczyny skalarne
self.a_2 = self.g2(z_2) return self.a_2 def get_error(self,x,trget): self.bl = for i in range(,self.x.shape[]): # weźmy przykład i-ty x = self.x[i,:].reshape(self.n_wej,1) y = self.y[i,:].reshape(self.n_wyj,1) self.a_2 = self.predict(x) # delta d_2 = (self.a_2 - y)*self.g2_prim(self.a_2) self.bl += np.dot(d_2.t,d_2)/self.x.shape[] return self.bl def fit_one_step(self, eta1,eta2): self.bl = D_1 = np.zeros((self.n_hid, self.n_wej+1)) D_2 = np.zeros((self.n_wyj, self.n_hid+1)) for i in range(,self.x.shape[]): # weźmy przykład i-ty x = self.x[i,:].reshape(self.n_wej,1) y = self.y[i,:].reshape(self.n_wyj,1) self.a_2 = self.predict(x) # propagacja "wstecz" d_2 = (self.a_2 - y)*self.g2_prim(self.a_2) d_1 = np.dot(self.w_2.t, d_2) * self.g1_prim(self.a_1)#z_2 # akumulujemy poprawki D_2 += np.dot( d_2, self.a_1.t) D_1 += np.dot( d_1[1:], self.a_0.t) self.bl += np.dot(d_2.t,d_2)/self.x.shape[] # uaktualniamy wagi self.w_1 -= eta1*d_1 + eta2*self.dw1 self.w_2 -= eta1*d_2+ eta2*self.dw2 self.dw1 = eta1*d_1 self.dw2 = eta1*d_2 return self.bl Proszę poeksperymentować z ilością i typem neuronów w warstwie ukrytej. Również ilość warstw ukrytych można zmieniać podając dodatkowe liczby pomiędzy parametrami określającymi rozmiar
wejścia i wyjścia. Jako punkt startu zastosujemy 3 domyślnych (sigmoidalnych) neuronów w warstwie ukrytej: S = siec( X, target, N_hid= 3) Przygotowujemy parametry: # liczba epok uczenia N_epochs = 150 eta1 = 0.005 eta2 = 0.9 Teraz przystępujemy do uczenia sieci (zapuścimy uczenie na 20 epok), przy czym po każdym kroku będziemy podglądać aktualny stan sieci, więc w metodzie trainepochs podajemy 1 krok. # inicjuję tablice na ewolucje err = np.zeros(n_epochs) #tablica na błąd zbioru uczącego err_m = np.zeros(n_epochs) #tablica na błąd zbioru monitorującego wagi = np.zeros((n_epochs,len(s.get_params()))) #tablica na wagi for cykl in range(n_epochs): err[cykl] = S.fit_one_step(eta1,eta2) # wykonaj krok uczenia err_m[cykl] = S.get_error(X_m,target_m)# normalizuję aby uzyskać średni błąd kwadratowy wagi[cykl,:] = S.get_params() #pobieram wagi do zapamiętania Narysujmy co działo się z błędem i wagami: # rysunki py.subplot(2,1,1) # błędów py.semilogy(err,'b',label='zb. uczacy') py.semilogy(err_m,'r',label='zb. monitorujacy') py.title(u'błąd') py.legend() #py.ylim([0,1]) py.subplot(2,1,2) #wag py.plot(wagi) py.title('wagi')
py.ylim([-3,3]) Rysunki dodatkowe Tu przygotowujemy siatkę punktów, które będziemy stosować do zilustrowania podziału przestrzeni wejściowej na obszary należące do poszczególnych klas. Funkcja meshgrid pobiera na wejście wektor dla x i y, a zwraca tablicę dwuwymiarową reprezentującą siatkę o brzegach x i y. ticks = arange(-3.,6.,0.2) X, Y = meshgrid(ticks, ticks) Teraz przygotujemy się do ilustrowania działania sieci graficznie. Przepuścimy przez sieć zbiór danych zawierających siatkę punktów. Dla każdego punktu otrzymamy w zmiennej out aktywność neuronów warstwy wyjściowej. out =... Za pomocą metody argmax() pobieramy indeks neuronu, który miał największą aktywność. out = out.argmax(axis=1) # the highest output activation gives the class Dopasowujemy kształt wyjścia do kształtu wejść. out = out.reshape(x.shape) Teraz możemy wykonać rysunek. hold(true) # włączamy opcję dopisywania do bieżącego rysunku for c in [,1,2]: # iterujemy się przez możliwe klasy here, _ =... # wybieramy indeksy punktów testowych należących do klasy c... # rysujemy kółkami punkty testowe należące do klasy c if out.max()!=out.min(): # safety check against flat field contourf(x, Y, out) # przy pomocy zapełnionych konturów rysujemy wynik klasyfikacji punktów siatki, daje nam to ilustrację obszarów na jakie sieć aktualnie dzieli przestrzeń wejściową py.show()
Polecenia dodatkowe Proszę zbadać powtarzalność granic separacji Proszę zbadać klasyfikację punktu odległego od zbioru uczącego: out = fnn.activate((100, 100)) print out Proszę zbadać zależność separacji i kształty powierzchni rozgraniczających w zależności od: liczby neuronów w warstwie ukrytej współczynnika weightdecay w trenerze Proszę sprawdzić działanie klasyfikatora dla innych konfiguracji klas wejściowych, np: łącząc kilka rozkładów normalnych o różnych parametrach w jedną klasę