|
Opdracht 2: Het Hopfield paradigma.
A 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 (en ook nog eens heel goed is) zullen we dit programma gebruiken in het practicum.
Om je te laten wennen aan Nutshell zullen we eerst een aantal simpele stappen doen. We gebruiken hierbij het Hopfield paradigma. 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 – je moet zelfs een keuze maken wil je de opstartprocedure kunnen afronden. Kies "Hopfield 82".
Je ziet nu een lege ‘workspace’ (een groot leeg venster waarin je een model kunt bouwen), en verschillende werkbalken met tal van functies. Belangrijk wordt 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. Onder de Help-functie (bovenste werkbalk) staat een Hopfield tutorial. Hierin staan een wat uitgebreidere beschrijving van een aantal functies.
Je gaat een Hopfieldnetwerk opzetten, trainen en het resultaat bekijken. Een Hopfieldnetwerk bestaat uit knopen die zich in een ‘laag’ (layer) bevinden.
1. 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 random geïnitialiseerde activaties. 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 knoop-nummer, en klik op het verschijnende veldje met ‘parameters’. Kijk wat het verschil is tussen rode en zwarte knopen. .
2. De knopen zijn nog niet met elkaar verbonden. Dit doe je door op het bovenste deel van de laag 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.
3. De waarden van de knopen kun je instellen door de functie van de muispointer in het edit menu 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.
4. Je kunt de hele layer of een deel van de layer de set waarde geven door een vierkantje om het te veranderen deel te trekken. De clear waarde kan worden veranderd door de shift knop ingedrukt te houden en dan een vierkantje om het te veranderen deel te trekken.
5. Als het je gelukt is de activatie-waarden onder controle te krijgen dan kun je proberen een patroon in de laag te tekenen.
6. Door middel van Learn in het linker menu wordt dit patroon geleerd. Je kunt op twee manieren controleren of de connecties ook daadwerkelijk veranderd zijn.
a. 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.
b. Je kunt het netwerk testen.
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 "Step" voer je een enkele berekening uit. Met Cycle voer je er een aantal achter elkaar uit. Na een aantal keren op cycle te hebben geklikt krijg je je originele patroon terug.
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. Scripten zijn eigenlijk kleine programmaatjes. Ze zijn vergelijkbaar met macro’s 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 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 (natuurlijke) taal een bepaalde syntax. Wat je doet is communiceren met een ‘compiler’. Deze compiler kan alleen dingen begrijpen als jij je aan de syntax houdt. De compiler vertaalt de syntaxconstructies in de voor de hardware van de computer begrijpelijke code, die uiteindelijk al jouw instructies zal gaan uitvoeren. (Ik heb voor het gemak allerlei tussenstappen overgeslagen). 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/definieren, 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 misschien het handigst 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 makelijk 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 gedefineerd en die je wilt gaan gebruiken in de subprocedure. Schematisch kan een programma er ongeveer zo uit zien (gereserveerde woorden zijn schuin gedrukt):
Sub Hoofprocedure
Dim Datastructuur1
Dim Datastructuur2
Subprocedure1 datastructuur1 datastructuur2
Dim datastructuur3
Subprocedure2 datastructuur2 datastructuur3
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
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
D. Practicumopdrachten
De eerste opdracht kun je gewoon uitvoeren in de Nutshell omgeving. In opdracht 2.2 leer je met scripts werken en zal je wat aan de daar beschreven scripts moeten veranderen.
2.1 Interferentie
a) Attractor netwerken hebben vaak last van zogenaamde spurious states. Dit zijn stabiele patronen die nooit zijn aangeleerd. Leer een netwerk een patroon aan door knopen in het patroon actief te maken en vervolgens 1 keer (en niet meer) op "Learn" te drukken Wat is de belangrijkste spurious state in een hopfield netwerk dat maar één patroon heeft geleerd?
Hint: ‘reset’ de layer met een random patroon, kijk naar welk patroon het netwerk convergeert. Als je het niet meteen ziet, varieer de grootte van je patroon (=het aantal actieve knopen in je patroon).
b) Leer je een netwerk teveel patronen, dan gaan ze elkaar storen. Dit wordt wel ‘interferentie’ genoemd. Het aantal patronen dat een netwerk kan leren is afhankelijk van het aantal neuronen in het netwerk.
Maak een netwerk aan van 5 bij 5 knopen. Probeer uit te vinden met dit netwerk hoe veel patronen je het kunt leren voordat er sprake is van teveel interferentie. Met andere woorden, voor het netwerk patronen gaat verliezen bij het leren van een nieuw patroon. Maak de patronen hierbij zelf door ze te tekenen en vervolgens 1 keer op "Learn" te drukken. Test, na elk nieuw patroon alle patronen door ong.90% van de knopen binnen het patroon actief te maken. Geef aan hoeveel patronen je kan leren, en welk percentage dit is van het aantal knopen in het netwerk. Teken (of ‘screendump’) ook de zelfgemaakte patronen.
2.2 Graceful degradation
Je gaat in deze opdracht werken aan een script waarin "graceful degradation" wordt getest. Connectionistische netwerken zijn verrassend resistent tegen beschadigingen: ook bij flinke lesies in hun gewichtenmatrix zijn ze nog steeds in staat heel behoorlijk de geleerde patronen op te halen. In het script waarmee je gaat werken worden drie patronen geleerd (zie Figuur 1). Vervolgens worden lesies aangebracht in de gewichtenmatrix, en wordt gecontroleerd in hoeverre de patronen nog door het model opgehaald kunnen worden. We hebben het script opgedeeld in delen, beginnend bij de romp van de simulatie en daar steeds verder op voortbouwend.
Open het bestand HopfieldVraag2a.xls. Voordat het bestand wordt geopend moet je eerst kiezen of macro’s ingelezen kunnen worden of niet. Klik op "Enable Macros". Nu moet de Visual Basic (VB) editor worden opgestart om de code van het script te bekijken en eventueel te veranderen. Ga naar "Tools" in de menubalk, hierin naar "Macro", en kies onder Macro de optie "Visual Basic Editor". Je kunt ook de "Alt" en "F11"-toetsen samen indrukken.
a) Kijk naar het script, probeer het door te lopen van begin tot eind. Breidt nu het script zo uit dat er twee extra patronen van hetzelfde type worden geleerd als de drie die al geleerd worden. Let op: deze twee extra patronen moeten niet alleen worden aangemaakt, er moet ook nog opdracht worden gegeven dat twee extra patronen moeten worden geleerd. Dit doe je door onder "Global parameters" de parameter ‘NrOfPatterns’ met 2 te verhogen.
Je kunt het programma runnen door in je VB editor op het blauwe "play" knopje te drukken (dit staat op het tweede balkje).
b) Open nu het HopfieldVraag2b.xls-bestand. Dit bestand is bijna identiek aan HopfieldVraag2a.xls, met als toevoegingen een extra globale parameter en code die een lesie maakt na leren (deze code is echter nog niet functioneel).
Verander het programma zodanig dat er lesies worden gemaakt. Zorg dat het aantal gewichten dat geledeerd wordt toeneemt met telkens 500 – dus dat er eerst 500 connecties worden geledeerd, vervolgens 1000 etc. Doe dit tot het maximaal aantal lesies wat je kunt maken (je kunt natuurlijk niet meer lesies maken dan er connecties zijn).
Om dit te bewerkstelligen moet je twee variabelen veranderen in de procedure "HopfieldVraag 2b". Beschrijf deze en geef de waarde die je ze gegeven hebt.
c) Open nu het HopfieldVraag2c.xls-bestand. Het zou handig zijn om te kijken wat de effecten zijn van de verschillende lesies. Om de effecten van de lesie te testen is er een Testprogramma gemaakt. Als maat wordt hiervoor de Hamming-distance gebruikt. Zoek uit wat de Hamming-distance is.
(Hint: Dit kun je uit de code halen. Kijk naar waar in het programma de Hamming-distance berekend wordt, en probeer te begrijpen wat er opgeteld wordt).
d) Open nu het HopfieldVraag2d.xls-bestand. Maak in dit programma de veranderingen die je in vraag 2a en 2b hebt gemaakt. Om de testresultaten naar Excel weg te laten schrijven is er een procedure WriteHammingdistance geschreven. Als je het programma nu runt worden de resultaten naar een Excel sheet met de naam UitdraaiHopfieldVraag2d.xls weg geschreven. Wil je meerdere keren het programma runnen voor meer resultaten dan moet je de naam van deze file in het script veranderen (in bijvoorbeeld Uitdraai2HopfieldVraag2d.xls).
Run het script minstens twee maal, zodat je twee of meer verschillende sets resultaten hebt. Vergelijk ze; is er veel variantie in de resultaten? Bij hoeveel lesies beginnen de patronen werkelijk te verdwijnen? Hoe verloopt het verval? Als werkelijke bolleboos fit je verschillende functies om de beste beschrijving te vinden (kan binnen excel als je een grafiek aanmaakt).
Lever de file "HopfieldVraag2d.xls" in met je veranderingen, de resultaten, en je antwoorden op de vragen.
Veel succes!
!!!!! DEADLINE: VOOR 12 MEI
MOET HET INGELEVERD ZIJN !!!!!
|