
Uczenie maszynowe i sztuczne sieci neuronowe/Ćwiczenia 7: Różnice pomiędzy wersjami
|  (Utworzono nową stronę " =Wstęp= Celem tych ćwiczeń jest zapoznanie się z funkcjonalnością PyBrain wspierającymi klasyfikację za pomocą sieci neuronowych.   =Zbiór uczący = PyBrain p...") | m (→Klasyfikacja) | ||
| (Nie pokazano 9 pośrednich wersji utworzonych przez tego samego użytkownika) | |||
| Linia 1: | Linia 1: | ||
| =Wstęp= | =Wstęp= | ||
| − | Celem tych ćwiczeń jest zapoznanie się z  | + | Celem tych ćwiczeń jest zapoznanie się z  klasyfikacją za pomocą sieci neuronowych. | 
| + | == Importy == | ||
| + | <source lang = python> | ||
| + | # -*- 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 | ||
| + | |||
| + | </source> | ||
| =Zbiór uczący = | =Zbiór uczący = | ||
| − | + | Funkcja generująca zbiór uczący: | |
| − | |||
| − | + | <source lang = python> | |
| − | <source lang = | + | def gen(ile): | 
| − | + |     mu = [(-1,0),(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) | ||
| + | |||
| </source> | </source> | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | 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 [[Uczenie_maszynowe_i_sztuczne_sieci_neuronowe/Wykład_6#Regresja_softmax_jako_GLM | regresji wielorakiej (softmax)]].   | |
| + | <source lang = python> | ||
| + | def convert_to_target(Y): | ||
| + |     target = np.zeros((len(Y), len(np.unique(Y)))) | ||
| + |     for i in range(len(Y)): | ||
| + |         target[i,Y[i,0]] = 1 | ||
| + |     return target | ||
| </source> | </source> | ||
| − | + | Testujemy: | |
| − | <source lang =  | + | <source lang = python> | 
| − | + | X,Y = gen(5) | |
| − | print  | + | print(Y) | 
| − | [ | + | py.scatter(X[:,0],X[:,1],c = Y,s = 10) | 
| − | + | target = convert_to_target(Y) | |
| − | + | print(target) | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| </source> | </source> | ||
| =Klasyfikacja= | =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. | 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:  | ||
| − | + | <source lang = python> | |
| − | <source lang =  | + | 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.figure() | |
| + | py.scatter(X[:,0],X[:,1],c = Y,s = 10) | ||
| + | py.scatter(X_m[:,0],X_m[:,1],c = Y_m, s = 10, marker = 's') | ||
| </source> | </source> | ||
| − | + | Warto przekodować te dane tak aby jedna klasa była reprezentowana przez jeden neuron wyjściowy:   | |
| − | <source lang =  | + | <source lang = python> | 
| − | + | target = convert_to_target(Y) | |
| − | + | target_m = convert_to_target(Y_m) | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| </source> | </source> | ||
| − | |||
| − | <source lang =  | + | Teraz wytworzymy sieć.  Rozmiar wejścia i wyjścia muszą się zgadzać z rozmiarami danych wejściowych i wyjściowych, odpowiednio.  | 
| − | + | <source lang = python> | |
| − | + | 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) | ||
| + |         return y    | ||
| + |     def g1_prim(self, x): | ||
| + |         y = x*(1-x) | ||
| + |         return y | ||
| + |     def g2(self, x): | ||
| + |         y = x | ||
| + |         return y    | ||
| + |     def g2_prim(self, x): | ||
| + |         y = 1 | ||
| + |         return y | ||
| + |     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 = 0 | ||
| + |         for i in range(0,self.X.shape[0]): | ||
| + |             # 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[0] | ||
| + |         return self.bl | ||
| + | |||
| + |     def fit_one_step(self, eta1,eta2): | ||
| + |         self.bl = 0 | ||
| + |         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(0,self.X.shape[0]): | ||
| + |             # 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[0] | ||
| + |         # 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 | ||
| + | |||
| </source> | </source> | ||
| − | |||
| − | <source lang =  | + | |
| − | + | 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:  | |
| + | |||
| + | <source lang = python> | ||
| + | S = siec( X, target, N_hid= 3) | ||
| </source> | </source> | ||
| − | + | Przygotowujemy parametry: | |
| − | <source lang =  | + | <source lang = python> | 
| − | + | # liczba epok uczenia | |
| − | + | N_epochs = 150 | |
| + | eta1 = 0.005 | ||
| + | eta2 = 0.9 | ||
| </source> | </source> | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | 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 <tt>trainEpochs</tt> podajemy 1 krok.   | |
| + | <source lang = python> | ||
| + | # 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 | ||
| − | |||
| − | |||
| − | |||
| − | + | py.figure() | |
| − | + | for cykl in range(N_epochs): | |
| − | + |     err[cykl] = S.fit_one_step(eta1,eta2) # wykonaj krok uczenia | |
| − | </source> | + |     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 | ||
| + | </source>   | ||
| + | Narysujmy co działo się z błędem i wagami: | ||
| + | <source lang = python> | ||
| + | #  rysunki | ||
| + | py.figure() | ||
| + | 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]) | ||
| + | </source>  | ||
| + | ==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 <tt>meshgrid</tt> pobiera na wejście wektor dla x i y, a zwraca tablicę dwuwymiarową reprezentującą siatkę  o brzegach x i y.   | 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 <tt>meshgrid</tt> pobiera na wejście wektor dla x i y, a zwraca tablicę dwuwymiarową reprezentującą siatkę  o brzegach x i y.   | ||
| − | + | ||
| − | <source lang =  | + | <source lang = python> | 
| ticks = arange(-3.,6.,0.2) | ticks = arange(-3.,6.,0.2) | ||
| X, Y = meshgrid(ticks, ticks) | X, Y = meshgrid(ticks, ticks) | ||
| − | + | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| </source> | </source> | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| 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 <tt>out</tt> aktywność neuronów warstwy wyjściowej.   | 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 <tt>out</tt> aktywność neuronów warstwy wyjściowej.   | ||
| − | <source lang =  | + | <source lang = python> | 
| − |      out =  | + |      out = ... | 
| </source> | </source> | ||
| Za pomocą metody <tt>argmax()</tt> pobieramy indeks neuronu, który miał największą aktywność.   | Za pomocą metody <tt>argmax()</tt> pobieramy indeks neuronu, który miał największą aktywność.   | ||
| − | <source lang =  | + | <source lang = python> | 
|      out = out.argmax(axis=1)  # the highest output activation gives the class |      out = out.argmax(axis=1)  # the highest output activation gives the class | ||
| </source> | </source> | ||
| Dopasowujemy kształt wyjścia do kształtu wejść. | Dopasowujemy kształt wyjścia do kształtu wejść. | ||
| − | <source lang =  | + | <source lang = python> | 
|      out = out.reshape(X.shape) |      out = out.reshape(X.shape) | ||
| </source> | </source> | ||
| Teraz możemy wykonać rysunek. | Teraz możemy wykonać rysunek. | ||
| − | <source lang =  | + | <source lang = python> | 
| − |      figure( | + |      py.figure() | 
| − | + | ||
| − | + | ||
|      hold(True) # włączamy opcję dopisywania do bieżącego rysunku |      hold(True) # włączamy opcję dopisywania do bieżącego rysunku | ||
|      for c in [0,1,2]:  # iterujemy się przez możliwe klasy |      for c in [0,1,2]:  # iterujemy się przez możliwe klasy | ||
| − |          here, _ =  | + |          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 |      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ą |          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() | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | show() | ||
| </source> | </source> | ||
Aktualna wersja na dzień 18:53, 6 cze 2017
Spis treści
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,0),(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,0]] = 1
    return target
Testujemy:
X,Y = gen(5)
print(Y)
py.scatter(X[:,0],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.figure()
py.scatter(X[:,0],X[:,1],c = Y,s = 10)
py.scatter(X_m[:,0],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)
        return y   
    def g1_prim(self, x):
        y = x*(1-x)
        return y
    def g2(self, x):
        y = x
        return y   
    def g2_prim(self, x):
        y = 1
        return y
    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 = 0
        for i in range(0,self.X.shape[0]):
            # 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[0]
        return self.bl
        
    def fit_one_step(self, eta1,eta2):
        self.bl = 0
        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(0,self.X.shape[0]):
            # 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[0]
        # 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
py.figure()
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.figure()
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.
    py.figure()
   
    
    hold(True) # włączamy opcję dopisywania do bieżącego rysunku
    for c in [0,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ę

