Zapisz jako PDF

Transkrypt

Zapisz jako PDF
Spis treści
1 Wstęp
2 Zbiór uczący
3 Klasyfikacja
4 Polecenia dodatkowe
Wstęp
Celem tych ćwiczeń jest zapoznanie się z funkcjonalnością PyBrain wspierającymi klasyfikację za
pomocą sieci neuronowych.
Zbiór uczący
PyBrain posiada klasę do obsługi zbiorów uczących przeznaczonych do klasyfikacji:
ClassificationDataSet. Jest ona dostarczana przez moduł
pybrain.datasets.classification.
Podstawowa składnia to:
DS = ClassificationDataSet(inputdim, nb_classes=2,
class_labels=['Fish','Chips'])
tzn. musimy zdefiniować:
rozmiar wektorów wejściowych inputdim
liczba klas nb_classes
opcjonalnie możemy podać nazwy klas: class_labels
Wymiar docelowy ma być 1. Celem są etykiety klasy zaczynające się od zera. Jeśli z jakiegoś powodu
nie wiemy wcześniej, ile będzie klas, możliwe jest ustawienie tej informacji później, używając metod
assignClasses() lub calculateStatistics() tak jak w poniższym przykładzie:
DS = ClassificationDataSet(2, class_labels=['sredni', 'duzy', 'maly'])
DS.appendLinked([ 0.1, 0.5 ]
, [])
DS.appendLinked([ 1.2, 1.2 ]
, [1])
DS.appendLinked([ 1.4, 1.6 ]
, [1])
DS.appendLinked([ 1.6, 1.8 ]
, [1])
DS.appendLinked([ 0.10, 0.80 ] , [2])
DS.appendLinked([ 0.20, 0.90 ] , [2])
print DS.calculateStatistics()
print DS.classHist
print DS.nClasses
print DS.getClass(1)
print DS.getField('target').transpose()
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). ClassificationDataSet posiada metodę _convertToOneOfMany pozwalającą
na automatyczne przekodowanie klas z numeracji
na kodowanie zer i jedynek na
odpowiednich wyjściach.
DS._convertToOneOfMany(bounds=[, 1])
print DS.getField('target')
[[1 ]
[ 1 ]
[ 1 ]
[ 1 ]
[ 1]
[ 1]]
print DS.getField('class').transpose()
[[ 1 1 1 2 2]]
>>> DS._convertToClassNb()
>>> print DS.getField('target').transpose()
[[ 1 1 1 2 2]]
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.
Najpierw przygotowyjemy grafikę do pracy w trybie interaktywnym
# -*- coding: utf-8 -*import matplotlib
matplotlib.use('TkAgg')
Następnie musimy zaimportować z bibliotek potrzebne klasy i definicje:
from
from
from
from
from
from
from
from
pybrain.datasets
import ClassificationDataSet
pybrain.utilities
import percentError
pybrain.tools.shortcuts
import buildNetwork
pybrain.supervised.trainers import BackpropTrainer
pybrain.structure.modules
import SoftmaxLayer
pylab import ion, ioff, figure, draw, contourf, clf, show, hold, plot
scipy import diag, arange, meshgrid, where
numpy.random import multivariate_normal
Teraz 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:
mu = [(-1,),(2,4),(3,1)]
cov = [diag([1,1]), diag([0.5,1.2]), diag([1.5,0.7])]
alldata = ClassificationDataSet(2, 1, nb_classes=3)
for n in xrange(400):
for klasa in range(3):
input = multivariate_normal(mu[klasa],cov[klasa])
alldata.addSample(input, [klasa])
Teraz dzielimy ten zbiór danych uczących na część treningową i testową. Podzielimy go w proporcji
3/4 danych uczących i 1/4 testowych. Można oczywiście od razu wygenerować dwa osobne zbiory tak
jak to robiliśmy na poprzednich zajęciach. Tu jednak zaprezentujemy narzędzie wspomagające ten
proces dostępne w PyBrain.
tstdata, trndata = alldata.splitWithProportion( 0.25 )
Warto przekodować te dane tak aby jedna klasa była reprezentowana przez jeden neuron wyjściowy
(porównaj: regresja softmax).
trndata._convertToOneOfMany( )
tstdata._convertToOneOfMany( )
Możemy wypisać trochę informacji o naszym zbiorze danych:
print
print
print
print
"Ilość przykładów treningowych: ", len(trndata)
"Rozmiary wejścia i wyjścia: ", trndata.indim, trndata.outdim
"Pierwszy przykład (wejście , wyjście, klasa):"
trndata['input'][], trndata['target'][], trndata['class'][]
Teraz wytworzymy sieć. Skorzystamy ze skrótu buildNetwork. Rozmiar wejścia i wyjścia muszą się
zgadzać z rozmiarami danych wejściowych i wyjściowych, odpowiednio. Do klasyfikacji najlepiej w
warstwie wyjściowej umieścić warstwę typu softmax.
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 5 domyślnych (sigmoidalnych) neuronów w
warstwie ukrytej:
fnn = buildNetwork( trndata.indim, 5, trndata.outdim, outclass=SoftmaxLayer )
Przygotowujemy trenera w standardowy sposób:
trainer = BackpropTrainer( fnn, dataset=trndata, momentum=0.1, verbose=True,
weightdecay=0.01)
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. Punkty
siatki pakujemy do obiektu typu ClassificationDataSet, aby było je łatwo przepuszczać przez
sieć. Ponieważ musimy dodawać do tego obiektu pojedyncze punkty a nie całe tablice to trzeba
tablcę spłaszczyć metodą ravel (równoważnie można by zaimplementować tworzenie tego obiektu
w dwóch pętlach przebiegających odpowiednio x i y ). Pomimo, że nie interesuje nas teraz klasa tych
punktów coś musimy wpisać (np. 0) i przekodować ten zestaw danych na typ OneOfMany, żeby
można było łatwo skorzystać z funkcji przepuszczających zbiory danych przez sieć.
ticks = arange(-3.,6.,0.2)
X, Y = meshgrid(ticks, ticks)
# need column vectors in dataset, not arrays
griddata = ClassificationDataSet(2,1, nb_classes=3)
for i in xrange(X.size):
griddata.addSample([X.ravel()[i],Y.ravel()[i]], [])
griddata._convertToOneOfMany()
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.
for i in range(20):
trainer.trainEpochs( 1 )
Sprawdzamy działanie sieci na zbiorze uczącym i na testowym. Skorzystamy z metody trenera
testOnClassData() do policzenia aktualnej klasyfikacji dla zbioru uczącego (domyślnie) i dla
danych testowych. Funkcja percentError oblicza procentową rozbieżność pomiędzy swoimi
argumentami. Wyniki wypisujemy na konsoli.
trnresult = percentError( trainer.testOnClassData(), trndata['class'] )
tstresult = percentError( trainer.testOnClassData(dataset=tstdata ),
tstdata['class'] )
print "krok: %4d" % trainer.totalepochs, \
" błąd na zbiorze uczącym: %5.2f%%" % trnresult, \
" błąd na zbiorze testowym: %5.2f%%" % tstresult
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 = fnn.activateOnDataset(griddata)
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.
figure(1)
ioff() # wyłączamy tryb interaktywny grafiki
clf()
# czyścimy rysunek
hold(True) # włączamy opcję dopisywania do bieżącego rysunku
for c in [,1,2]: # iterujemy się przez możliwe klasy
here, _ = where(tstdata['class']==c)
# wybieramy indeksy
punktów testowych należących do klasy c
plot(tstdata['input'][here,],tstdata['input'][here,1],'o') # 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ą
ion()
# przełączamy grafikę w tryb interaktywny
draw() # odświeżamy rysunek
Po zakończeniu uczenia czekamy aż użytkownik zamknie ostatni obrazek.
ioff()
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ę