III.
Werken met Nutshell
Tot voor kort schreven alle connectionisten hun
eigen simulatieprogramma's, met eigen algorithmes, eigen opties, eigen
leerregels, outputmodules, patrooninlezers, etc. Iedereen kon zo zijn code aan
de eigen wensen aanpassen, maar het resultaat was wel dat het wiel telkens
opnieuw uitgevonden werd. Sinds enige jaren komen er
standaardsimuleeromgevingen beschikbaar. Idealiter maken die het mogelijk om
zonder eerst maanden te programmeren meteen simulaties te kunnen doen, zonder
de flexibiliteit aan te tasten die de gevorderde gebruiker nodig heeft.
Nutshell is zo'n simuleeromgeving. Er is één buitenkant waarmee je veel
verschillende connectionistische paradigma's (=soorten netwerken) kunt
simuleren. Aangezien Nutshell toevallig ontwikkeld werd aan de UvA [1]
(en ook nog eens heel goed is) zullen we dit programma gebruiken in het
practicum.
A. Werken met de omgeving
Om je te laten wennen aan Nutshell zullen we eerst
een aantal simpele stappen doen. We gebruiken hierbij het Hopfield paradigma
dat ook in de eerstvolgende opdracht zal worden gebruikt. Je start Nutshell op door
in je startknop Nutshell aan te klikken (staat onder 'Programs' en dan
'Nutshell 1.0'). De nutshell-applicatie start nu op. Je kan uit verschillende
paradigma's kiezen. Een paradigma is in connectionisme een bepaalde manier
waarop netwerken werken - een definitie van hoe leren gaat, hoe knopen
ge-update worden, wat je allemaal met lagen kunt doen, etc. Hopfield is zo'n
paradigma. Kies "Hopfield.np".
Opmaak van de nutshell-omgeving
Je ziet nu een lege 'workspace' (een groot leeg
venster waarin je een model kunt bouwen), en verschillende werkbalken met tal
van functies. Onder de Help-functie (in de menu's bovenin) staat de op het
moment nog vrij beperkte, helpinformatie van nutshell, en verder een Hopfield
tutorial. Hierin staan een wat uitgebreidere beschrijving van een aantal
functies. Belangrijk is ook het 'Edit'-menu, te herkennen aan de muispointer
met daarnaast een loep waar je mee in en uit kan zoomen. Als 'default' is de
muispointer geselecteerd. Verder belangrijk: het "General"-menu links
waarin allefuncties staan opgesomd. Als je een klein venster hebt dan kan je de
onderkant van het "General"-menu niet zien. Dat is niet
problematisch: alles in het "General"-menu staat ook onder
"Parameter" in de menubalk boven. Mocht je dus dingen niet kunnen zien
in het "General"-menu omdat het niet op je scherm past, kijk dan
onder "Parameter".
Links onderin nutshell zit het 'console'-venster, en
rechts onderin het outputvenster. Je kunt de console gebruiken om
scripting-commando's (zie sectie B) één voor één in te voeren en het
outputvenster om variabelen of andere informatie te zetten, maar beide vensters
gaan we niet veel gebruiken in deze cursus.
Een Hopfieldnetwerk bestaat uit knopen die zich in
een 'laag' (layer) bevinden. Maak een laag aan door simpelweg op "insert
layer" te klikken. Je mag aangeven welke dimensies de laag gaat krijgen;
standaard is 10 bij 10. Klik op "OK". Je ziet nu vierkant van knopen
(de laag) met willekeurig geïnitialiseerde activaties.
Variabelen bekijken
Je kunt de waarde van de activatie -en van andere
parameters- bekijken door een knoop te selecteren (1 keer erop klikken met de
linkermuisknop) en dan op de rechtermuisknop te drukken. Er verschijnt nu een
menu. Zet de muis op het veldje met 'Node' en het knoopnummer, en klik op het verschijnende
veldje met 'parameters'. Kijk wat het verschil is tussen rode en zwarte knopen.
Je kunt nu ook een knoop van rood naar zwart veranderen, door de waarde van de
goede parameter te veranderen en op "OK" te drukken.
Er zijn algemene parameters, parameters die bij
knopen horen, parameters die bij lagen horen, bij verbindingen, en bij 'tracts'
(bundels verbindingen van 1 laag naar zichzelf of een andere laag). Links in
het 'General'-menu is een optie 'Parameters'. Als je daarop klikt kan je de algemene
parameters bekijken. 'Laag'-parameters kan je inkijken door een laag aan te
wijzen door er op te klikken, dan weer de rechtermuisknop in te drukken, en
onder de "Layer"-optie te kijken. Daar zal je weinig aantreffen:
'Hopfield' is een paradigma met weinig parameters (er is bijvoorbeeld geen
enkele 'tract'-parameter). Andere paradigma's hebben hele lijsten parameters.
De laag verbinden, patronen tekenen
De knopen zijn nog niet met elkaar verbonden. Dit
doe je door op het bovenste deel van de laag (het witte) te klikken om hem te
selecteren, en dan links in het menu op "Insert Tract" te klikken. Er
verschijnt een ronde pijl rechtsboven de laag. Dit is de 'tract' met de
verbindingen van de laag naar zichzelf. Het netwerk is nu intern volledig verbonden.
De waarden van de knopen kun je instellen door de
functie van de muispointer in het editmenu in "set value" te
veranderen. Deze functie staat meest rechts (half gevuld vierkantje). Nu kun je
met je linker muisknop de ene uiterste waarde selecteren en met je rechter
muisknop de andere. Deze waarden stel je in door op het pijltje rechts van het
half gevulde vierkantje te klikken. Kies "custom value". Zorg dat de waardes
gelijk zijn aan de twee mogelijke activatiewaardes in het Hopfield-paradigma:
"set" moet +1 zijn, en "clear" -1.
Als je nu op een rode knoop klikt zal hij zwart
worden ('set'-waarde). Je kunt de hele laag of een deel van de laag tegelijk de
'set'-waarde geven door het 'vlakje trekken'. Zet de muispunt buiten de laag
(maar in de buurt), houd de linkermuisknop ingedrukt en schuif de muispunt over
een deel van de laag. In het getrokken vierkantje krijgen nu alle knopen de
'set'-waarde. Je kunt de 'clear'-waarde aan knopen geven door de shift knop
ingedrukt te houden en of op een knoop te klikken, of weer een vlak te trekken
om een te veranderen deel van de laag.
Als het je gelukt is de activatie-waarden onder
controle te krijgen dan kun je proberen een patroon in de laag te tekenen.
Leren in het netwerk, bekijken van gewichten
Door middel van "Learn" in het linker menu
wordt dit patroon geleerd. Druk op "Learn".
Je kunt op twee manieren controleren of de
connecties ook daadwerkelijk veranderd zijn. Je kunt de connecties bekijken.
Dat doe je door boven in het scherm bij View naar de optie connection te gaan.
De eerste keer dat je dit doet zie je misschien alleen maar grijze vierkantjes
met kruisjes erin. Dan moet je even de functie van de muispointer veranderen in
het simpele pijltje. Als je nu op een neuron in de laag klikt zie je de waarde
van de verbindingen met andere neurons als kleuring van deze neurons. Een nader
onderzoek is mogelijk als volgt. Klik op een neuron naar keuze. Klik op een
ander neuron naar keuze maar houd daarbij de shift toets ingedrukt. Als je nu
de rechtermuisknop indrukt krijg je in het menu de optie
"weight(nodenummer, nodenummer)" erbij. Als je deze optie aanklikt
zie je de waarde die de verbinding heeft aangenomen.
Activatie updaten, iteraties
In het simpele geval van een geleerd patroon is het
makkelijk aan te tonen dat het netwerk het patroon heeft onthouden. Hiervoor ga
je terug naar de "node"-mode (bovenaan: view>node). Nu reset je de
layer met random waarden (hiervoor eerst de laag selecteren door klikken op de
juiste plaats of het "vlakje trekken". Klik links op "reset layer"
Met de linker optie "Act Step" voer je een enkele berekening uit. In het geval
van een Hopfield-netwerk verandert dan maar de activatie van een enkele knoop.
Druk een aantal malen op "Act Step" tot je een knoop van activatie
ziet veranderen.
Met "Act Cycle" voer je er een aantal
iteraties achter elkaar uit ('default' 100 in Hopfield). Druk een keer op
"Act Cycle", en constateer dat er dan een heel stel knopen van
activatie veranderen. Na een aantal keren op 'Act Cycle" te hebben geklikt
krijg je je originele patroon terug. Als je het aantal iteraties wilt instellen
moet je onder "Parameters" kijken in het "General"-menu
links. Daar zal je de parameter "Cycle Step" vinden (in oudere
versies van Nutshell heet die "Iteration Length").
Clampen en deactiveren
Soms is het nodig een deel van een netwerk te laten
itereren, terwijl een ander deel constant dezelfde activatie houdt
(bijvoorbeeld als dit gedeelte de input van het netwerk weergeeft en je de rest
van het netwerk zich wilt laten aanpassen aan de input). Hiervoor bestaat in
Nutshell het 'clampen' van knopen. Een 'geclampte' knoop verandert niet van
waarde als je op "Act Cycle" drukt, ook als hij het normaal wel zou
doen. Knopen 'clampen' kan je door een kruisje te zetten achter "Clamped"
in het parametermenu van de knoop (zie onder 'Variabelen bekijken'). Veel
handiger is weer "vlakje trekken".
Om dit te testen moet je eerst de laag weer
'resetten' (laag selecteren, functie aanklikken in linkermenu). Klik nu in het
"Edit" menu op het tekeningetje met een wit vierkant met een zwart
kruis erdoor, en een klein wit pijltje. Je muispunt is nu een 'clamper'
geworden. Druk in de buurt van je netwerk op de muisknop, en trek de muispunt
over een deel van de laag. Als je nu loslaat zie je een witte en zwarte kruizen
verschijnen door alle knopen in het door jou getrokken vlak. Deze knopen zijn
nu geclampt. Als je nu op "Act Cycle" drukt zal je zien dat een
aantal van de niet-geclampte knopen van activatie verandert, maar de geclampte
knopen niet. Door de shift-knop ingedrukt te houden terwijl je een vlakje trekt
kan je knopen weer ontclampen.
Druk op het pijltje in het "Edit" menu om
de muispointer terug te krijgen.
Je kunt knopen ook deactiveren (dood maken). Druk
hiervoor in het "Edit" menu op het tekeningetje met een grijs
vierkant met een wit kruis erdoor, en een witte pijltje. Je muispunt is nu een
'deactivator'. Trek weer een vlak over een deel van de laag. Je zult zien dat
de knopen in je vlak grijs worden met een wit kruis erdoor. Deze knopen zijn
gedeactiveerd: ze doen niets meer. Je kunt deactiveren, je raadt het al, weer
opheffen door de shift-knop ingedrukt te houden en weer een vlak te trekken.
B. Scripting
Je kunt met de functies die in het linker menu staan
verschillende soorten netwerken maken. Let wel, zolang je een
Hopfield-workspace hebt opgestart blijven dit altijd Hopfield netwerken met
Hopfield eigenschappen. Voor de rest kunnen deze netwerken zo complex worden
gemaakt als de gebruiker wil. Je kunt ook allerlei patronen in een netwerk
stoppen door ze na elkaar te tekenen en vervolgens te leren.
Voor onderzoek is het vaak nodig om je resultaten te
repliceren om te laten zien dat je resultaat geen toevalstreffer is maar een
robuust fenomeen. Om dan niet telkens het netwerk te hoeven maken en je patronen
te leren, kun je alle informatie over het netwerk en patronen in een script
zetten, een file met achter elkaar een serie commando's die nutshell kan
uitvoeren. Ook kun je via script je resultaten handig weg laten schrijven naar
bijvoorbeeld een excel sheet. Scripts zijn eigenlijk kleine programmaatjes. Ze
zijn vergelijkbaar met macro's in word processors, of syntax-files die je
misschien in SPSS hebt gebruikt.
Hoe je in een script met Nutshell werkt wordt uit de
doeken gedaan in Appendix 1. Daarin
staan een aantal commando's waarmee je dingen gedaan krijgt in Nutshell, met
uitleg over hun werking. De uitleg gaat er overigens wel van uit dat je iets
van visual basic begrijpt, dus bijv. de hieronder genoemde hoofdstukken van de
Microsoft webmanual gelezen hebt.
In de volgende sectie zullen we uitleggen hoe een
standaardscript in Visual Basic er uit ziet. Als je nog geen kaas gegeten hebt
van programmeren kan je dit gebruiken om de scripts die je in opdracht 2.2 moet
maken te begrijpen.
C. Het lezen van een Visual Basic-script.
In een net programma is er een scheiding tussen
datastructuren en algoritmen. In de datastructuren wordt de data die je
wilt gaan bewerken gezet. Algoritmen zijn de bewerkingen op de datastructuren.
Stel dat je je een rij van tien getallen wilt opslaan binnen Visual Basic (VB).
Dit definieer je zo:
Dim MaxSize as Integer
MaxSize = 9 ' 10 - 1
Dim GetalArray(MaxSize) as Integer
We beginnen met de onderste regel van de drie regels
code. Dim staat voor Dimension; dit is
in VB de aankondiging dat er een datastructuur gedefinieerd gaat worden.
GetalArray is je datastructuur, in dit geval een rij (array) van getallen.
MaxSize geeft de maximale hoeveelheid van getallen aan die je in de array kunt
stoppen. En 'as Integer' geeft aan wat voor soort getallen je in de array wilt
stoppen. Integers zijn de gehele getallen , de verzameling Z (zowel negatieve
als positieve getallen). Op de tweede regel code krijgt Maxsize een waarde
toegekend, namelijk de waarde 9. Het deel na het accent ' is commentaar (zie
verderop). Arrays beginnen in VB altijd bij element 0, dus als je tien
getallen op wilt slaan dan moet je een 'array' hebben van grootte 9: doordat je
het element 0 mee moet rekenen zitten er dan eigenlijk 10 elementen in de
Array. Op de bovenste regel wordt Maxsize gedefinieerd als een Integer.
Om nu daadwerkelijk getallen in de array te stoppen
kan het volgende algoritme worden geschreven:
For index = 0 to MaxSize
GetalArray(index) = index
Next
Op de eerste regel wordt een 'for' loop gedefineerd.
In een 'for'-loop wordt een stukje code een aantal malen herhaald; hier wordt
steeds 1 element van de array 'GetalArray' gelijk gezet aan de index. . Dit
wordt gedaan d.m.v. een index die de waarde 0 tot en met MaxSize kan aannemen.
Doordat steeds 1 element van GetalArray ingevuld wordt, wordt zo de hele
GetalArray doorlopen.
De eerste actie die de for loop doet is de
GetalArray op plaats 0 de waarde 0 toe te kennen. Dus:
GetalArray(0) = 0
Het Next statement zorgt ervoor dat er naar de
volgende index in de For loop wordt gegaan (We springen dus weer naar de eerste
regel). Na 0 komt 1, dus het volgende wat de for loop gaat doen is de
GetalArray op plaats 1 de waarde 1 te geven. Dus:
GetalArray(1) = 1
Dit gaat door totdat de 'For' loop bij MaxSize is
aanbeland. De index heeft dan de waarde van MaxSize, in dit geval 9. Het
laatste wat de for loop doet is GetalArray op plaats 9 de waarde 9 te geven,
daarna is de loop klaar en gaat het programma verder.
GetalArray(9) = 9
Een programmeertaal heeft net als elke taal een
syntax. Wat je doet is communiceren met een 'compiler', die je
programmeerconstructies vertaalt in voor de hardware van de computer
begrijpelijke code om jouw instructies uit te voeren (ik heb voor het gemak
allerlei tussenstappen overgeslagen). De compiler kan alleen dingen begrijpen
als jij je aan de syntax houdt. Elke programmeertaal heeft zijn eigen syntax,
maar in hun essentie zien ze er allemaal vrijwel hetzelfde uit. Een taal heeft
regels om datastructuren te maken/definiëren, en regels om algoritmen te
schrijven die deze datastructuren bewerken.
We zijn al een syntaxconventie van Visual Basic voor
datastructuren tegengekomen, namelijk dat na het woord Dim altijd een definitie
volgt van een datastructuur. 'Dim' is een zogenaamd gereserveerd woord, een
woord waarvan de betekenis is vastgelegd in de taal (zoals ook 'For' en 'Next' gereserveerde
woorden zijn). Een tweede datastructuur-conventie was dat als een woord meteen
wordt gevolgd door haakjes, (), het de naam van een array moet zijn. Die
arraynaam, en ook namen van andere typen variabelen, mag je geheel zelf verzinnen mits het geen gereserveerd woord is.
Dus sflj() is een array. Let wel dat er tussen haakjes maar één getal (die de
grootte aangeeft) mag staan wil het een array zijn, dus sflj(10) is een array.
Wat je vaak in de script zal tegenkomen is een matrix(aantalrijen, aantalkolommen).
Iets wat twee getallen tussen de twee haken heeft staan, zoals sflj(10, 3), is
een matrix volgens de Visual basic syntax.
Om algoritmen te schrijven heeft Visual Basic ook
allerlei vaste taalconstructies. De 'for…next' loop is hier een voorbeeld van.
In de visual basic 'help' kun je allerlei syntax-conventies voor datastructuren
en algoritmen opzoeken. Je kan dit doen door met je cursor het woord te
selecteren en dan op F1 te drukken. Of gewoon vanuit het helpmenu de help op te
starten en dan het woord in te typen. Bovendien is er op de website van
microsoft een heel boekwerk met informatie over Visual Basic te verkijgen. Voor
beginners is het goed om dit boekwerk (gratis) te downloaden en de eerste
twee-drie hoofdstukken door te lezen. Webadres: http://www.microsoft.com/officedev/articles/Opg/default.htm.
In een net programma zijn datastructuren en algoritmen netjes gescheiden. Dit is niet het geval in de visual basic scripts:
hier staan de datastructuren binnen de algoritmen. Maar een datastructuur is
makkelijk te herkennen omdat er Dim voor staat. Het algoritme zelf heeft een
duidelijke structuur. Er is sprake van een hoofdalgoritme of hoofdprocedure
waarin allerlei subprocedures staan. Subprocedures lossen vaak een deelprobleem
van het hoofdprobleem op. Deze subprocedures staan elders in het programma
gedefinieerd, en alleen de 'header' van de subprocedure staat in
hoofdprocedure. De header bevat de naam van een subprocedure en een aantal
parameters. Parameters zijn vaak datastructuren die in het hoofdprogramma zijn
gedefinieerd en die je wilt gaan gebruiken in de subprocedure. Schematisch kan
een programma er ongeveer zo uit zien (gereserveerde woorden zijn schuin gedrukt):
Sub Hoofdprocedure
Dim Datastructuur1
Dim
Datastructuur2
Subprocedure1 datastructuur1 datastructuur2
Dim datastructuur3
Subprocedure2 datastructuur2
datastructuur3
End Sub
SubSubprocedure1( Dim datastructuur1, Dim datastructuur2)
Doe iets met datastructuur1en datastructuur2
End Sub
Sub Subprocedure2( Dim datastructuur2, Dim datastructuur3)
Doe iets met datastructuur2 en datastructuur3
End Sub
In elk van de subprocedures kan ook weer van alles
worden gedaan. Er kan bijvoorbeeld een 'for…next' loop in staan, maar er kunnen
ook weer allerlei andere procedures worden aangeroepen. Die je dan weer elders
moet definiëren.
In een Visual basic script kun je nog drie andere
soorten van taalconstructies tegenkomen, namelijk script-aanroepen, globale constanten
en commentaar.
Scriptaanroepen zijn functie-aanroepen vanuit een
script-taal naar een ander programma, vaak een zgn. 'applicatieprogramma' zoals
Word of Excel, of ook Nutshell. Dit programma kan in een andere taal zijn
geschreven en bevat allerlei eigen datastructuren en algoritmen. In dit geval
worden er functies van de Nutshell-simulator aangeroepen vanuit Visual Basic.
Nutshell is in C++ geschreven en bevat tienduizenden regels code, maar
scripting maakt het mogelijk je daar niets van aan te trekken en er met Visual
Basic mee te werken.
Globale constanten zijn constanten die je overal in
je programma gebruikt. Als je bijvoorbeeld 5 patronen wilt leren, kan het
slimmer zijn een globale constante "NrOfPatterns"en maken en die gelijk te
zetten aan 5, dan om overal waar je het aantal patronen nodig hebt '5' te
schrijven.
Commentaar staat achter een accent: ' ('dit is
commentaar), en dient er meestal voor om aan te geven wat een bepaald algoritme
doet of een datastructuur representeert.
Schematisch kan een script er zo uit zien:
'Globale constanten
Const GlobalConstant1 = waarde
Const GlobalConstant2 = waarde
Sub Hoofprocedure
Scriptcommando1
Scriptcommando2
Dim Datastructuur1 ' Commentaar over wat deze datastructuur doet
Dim Datastructuur2 ' Commentaar over wat deze datastructuur doet
Subprocedure1 datastructuur1 datastructuur2
Dim datastructuur3
Subprocedure2 datastructuur2 data
End Sub
Sub Subprocedure1( Dim datastructuur1, Dim datastructuur2)
Doe iets met datastructuur1 en datastructuur2
End Sub
Sub Subprocedure2( Dim datastructuur2, Dim datastructuur3)
Doe iets met datastructuur2 en datastructuur3
Subprocedure3 datastructuur3
End Sub
Sub Subprocedure3( Dim datastructuur3)
Doe iets met datastructuur3
End Sub
[1]
Door Robert Berg, Eric Maryniak en Jaap Murre.
|