II.Werken met Nutshell
Tot voor kort schreven alle
connectionisten hun eigen simulatieprogrammas, met eigen
algoritmes, 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
doen, met behoud van de flexibiliteit die de gevorderde gebruiker
nodig heeft. Nutshell is zon simuleeromgeving. Er is één
buitenkant waarmee je veel verschillende connectionistische
paradigmas (=soorten netwerken) kunt simuleren. Aangezien
Nutshell toevallig ontwikkeld werd aan de UvA1
(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 start-menu Nutshell
aan te klikken (staat in onze Windows onder Programs en
dan Nutshell 1.0). De Nutshell-applicatie start nu op. Je
kan uit verschillende paradigmas 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 het Helpmenu (in
de menu's bovenin) staat de op het moment nog vrij beperkte
helpinformatie van Nutshell, en onder andere een Hopfield tutorial.
In de tutorial worden een aantal functies uitgebreidere besproken dan
hier, dus als je na dit hoofdstuk nog steeds weinig snapt van
Nutshell moet je die doorlopen. Belangrijk is ook de Edit-balk
(meestal rechts in Nutshell), 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: de "General"-balk
links waarin alle functies staan opgesomd. Als je een klein venster
hebt dan kan je de onderkant de het "General"-balk niet
zien. Dat is niet problematisch: alles in de "General"-balk
staat ook onder het "Parameter-menu in de menubalk boven.
Als je de General en Edit-balken niet ziet,
kijk dan onder het View-menu bovenaan of deze balken wel
aangevinkt zijn (de Edit-balk staat onder Toolbars).
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 de 'General'-balk 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 de balk 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 de editbalk in "set
value" te veranderen. Deze functie staat onderaan (half gevuld
vierkantje). 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. Nu kun je activatie van een knoop met een klik op je linker
muisknop op de set-waarde van 1 zetten, en met een klik
van je rechter muisknop op de clear-waarde van -1. Als je
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. Als je de
knop loslaat krijgen alle knopen in het getrokken vierkantje 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
de linkerbalk kan dit patroon worden 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 knoop in de laag klikt zie je de waarde van
de verbindingen met andere knopen als kleuring van deze knopen. Een
nader onderzoek is mogelijk als volgt. Klik op een knoop naar keuze.
Klik op een ander knoop 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 één
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 dan 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 soms 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 de "General"-balk 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 tweede gedeelte de constante
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 zien moet je eerst de laag
weer 'resetten' (laag selecteren, functie aanklikken in linkerbalk).
Klik nu in de "Edit"-balk 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 linker-muisknop, en trek de muispunt over een deel van
de laag. Als je nu loslaat zie je 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 de "Edit"
balk om de muispointer terug te krijgen.
Je kunt knopen ook deactiveren (dood
maken). Druk hiervoor in de "Edit" balk 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 de
linkerbalk staan verschillende 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 commandos die Nutshell kan
uitvoeren. Ook kun je via zon script je resultaten handig weg
laten schrijven naar bijvoorbeeld een Excel sheet. Scripts zijn
eigenlijk kleine programmaatjes. Ze zijn vergelijkbaar met macros
in word processors, of syntax-files die je misschien in SPSS hebt
gebruikt.
In de volgende sectie zullen we
uitleggen hoe een standaardscript in Visual Basic (VBA) er uit ziet. Als je
nog geen kaas gegeten hebt van programmeren kan je dit misschien
gebruiken om de scripts die je in opdracht 2.2 moet maken te
begrijpen. Meer introductie van VBA en ook over het werken
met VBA in Excel kun je vinden in:
C. Het lezen van een Visual Basic-script.
Programmeren draait om
datastructuren (arrays en variabelen) en algoritmen. In
de datastructuren wordt de data die je wilt gaan bewerken gezet.
Algoritmen zijn de bewerkingen op de datastructuren. Stel dat je een
rij van tien getallen (je data) 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 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 is ook een datastructuur, een variabele (een naam
voor iets dat kan variëren). We gebruiken deze variabele hier om
de maximale hoeveelheid van getallen aan te geven die je in de array
wilt stoppen. 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.
Je stopt een getal in een
datastructuur door de datastructuur gelijk te stellen aan het getal:
GetalArray(0) = 2
Het gelijkteken betekent hier niet,
zoals bij normale wiskunde, dat wat links staat gelijk is aan wat
rechts staat, maar dat wat links staat gelijk moet worden aan
wat rechts staat. In dit geval moet de eerste van de tien plekken in
array GetalArray (die plek wordt aangegeven als: GetalArray(0))
gelijk worden aan 2. In softwarespeak heet dit: Je kent de
waarde 2 toe aan het eerste element van de
array.
Stel nu dat je ook alle andere
elementen de waarde 2 wilt geven. Je kunt nu een aantal regels
schrijven:
GetalArray(0) = 2
GetalArray(1) = 2
. . .
GetalArray(9) = 2
Het is veel makkelijker de computer
in één keer te vertellen dat er tien keer een 2 aan een
element in de array moet worden gegeven. Dit kan met de
for
next-loop, een programmalus. In zon
lus wordt een bepaalde actie een aantal malen herhaald,
met soms elke keer een kleine aanpassing. Bijvoorbeeld in het
volgende algoritme:
For index = 0 to
MaxSize GetalArray(index) = 2 Next
In de for-loop wordt een
stukje code een aantal malen herhaald; hier wordt steeds één
element van de array GetalArray gelijk gezet aan de
waarde 2. De herhaling is niet letterlijk, omdat bij elke herhaling
van dat stukje code een ander element de waarde 2 krijgt. Dit doen we
met hulp van de index, de variabele die bijhoudt hoe vaak de
for
next-loop al doorlopen is (hier heet de index ook index,
maar dat hoeft niet; wij gebruiken vaak de lekker korte naam i).
Deze index heeft eerst de beginwaarde 0, en wordt dan in stapjes van
1 opgehoogd tot hij de waarde MaxSize heeft. We stellen steeds het
index-ste element van de array gelijk aan 2. Doordat
steeds 1 element van GetalArray ingevuld wordt, wordt zo de hele
GetalArray doorlopen.
De eerste actie die de For
Next
loop doet is de GetalArray op plaats index=0 de waarde 2 toe te
kennen. Dus:
GetalArray(0) = 2
Het Next statement zorgt ervoor dat
de loop nog eens gedaan wordt, en de index eentje wordt opgehoogd. We
springen dus weer naar de eerste regel, en de index wordt gelijk aan
1. Het volgende wat de for loop gaat doen is element index=1 van
GetalArray de waarde 2 te geven. Dus:
GetalArray(1) = 2
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 2 te geven, daarna is de loop klaar
en gaat het programma verder.
GetalArray(9) = 2
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 enen en nullen 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 variabelen, mag je geheel zelf verzinnen mits het
geen gereserveerd woord is. Dus sflj() is een array, terwijl sflj een
variabelenaam zou zijn. 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 (zie ook het gratis te downloaden Visual
Basic-boek
http://www.microsoft.com/officedev/articles/Opg/default.htm).
Een visual basic-scripts heeft een
duidelijke structuur. Er is sprake van een hoofdalgoritme of
hoofdprocedure waarin allerlei subprocedures staan. Het programma
begint op de eerste regel van de hoofdprocedure, en van daar uit
wordt de ene na de andere regel code afgewerkt. Subprocedures lossen
vaak een deelprobleem van het hoofdprobleem op. Deze subprocedures
staan elders in het programma gedefinieerd. In de hoofdprocedure
staat alleen een aanroep van de subprocedure daar waar je
de subprocedure wilt gebruiken. De aanroep is gelijk aan
de header (titelregel) van de subprocedure, en 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, namen van hoofd-en subprocedures vet):
Sub
Hoofdprocedure
Dim
Datastructuur1 Dim Datastructuur2 Subprocedure1
datastructuur1 datastructuur2
End Sub
Sub
Subprocedure1( Dim datastructuur1, Dim
datastructuur2)
Doe iets met
datastructuur1 en datastructuur2
End Sub
In een subprocedure kan 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
NrOfPatternste maken en die gelijk te zetten aan 5, dan
om overal waar je het aantal patronen nodig hebt 5 te
schrijven (iets soortgelijks deden we boven al, waar we de variabele
MaxSize gebruikten om het aantal elementen in de array GetalArray aan
te geven).
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
Dit is zo ongeveer de structuur van
de scripts in de opdrachten. Hoe zon 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 boven genoemde
hoofdstukken van de Microsoft webmanual gelezen hebt.
[1]
Door Robert Berg, Eric Maryniak en Jaap Murre.
|