dinsdag 27 februari 2018

Zelf optimaliserende modellen - 2

Ok, nu de methodiek eens testen op een iets groter model.
Ik heb een programmaatje gemaakt dat automatisch de getallen in een model omzet naar opeenvolgende variabelen maar daar maak ik nu nog even geen gebruik van. Dus een model maar handmatig aangepast.
Er zitten nu 3 conv layers in en 3 maxpooling layers alvorens naar een dense model over te stappen voor de laatste selectie.
De aanpassingen lopen redelijk voorspoedig maar ik loop al snel tegen een 'ongeldig model fout' aan. Hier loopt het programma op stuk. Het probleem is dat er nul of negatieve waarden in de netwerk grootte gaan voorkomen. Het verraste mij al dat ik nog niet dergelijke fouten in de vorige simulatie was tegen gekomen.
Ik pas het model aan zodat deze foutsituaties worden afgevangen. Het programma gaat dan terug naar de laatste 'beste oplossing' en probeert een nieuwe situatie uit. Dat lijkt aardig te werken.

conv1 .      maxp1   conv2 .      maxp2 . conv3 .       maxp3 . drop1 dense drop2   
32.0 3.0 3.0 2.0 2.0 32.0 3.0 3.0 2.0 2.0  64.0 3.0 3.0 2.0 2.0 0.25  128.0 0.5

39.0 4.0 4.0 2.0 2.0 22.0 3.0 4.0 2.0 2.0 271.0 2.0 3.0 2.0 2.0 0.16  153.0 0.81

Score p [0.04993261852805372, 0.9871]

Score pn [0.036428343426994615, 0.9918]

De score is nog niet zo hoog als bij het vorige model


De score is nog niet zo hoog als bij het vorige model maar wellicht moet er nu exponentieel meer mogelijkheden worden uitgetest. Opvallend is dat hier de conv1 verhoogd (32 --> 39) en de conv3 heel veel(64 --> 271). De maxpooling blijven opvallend constant.

Met een 2e ronde gaat het al veel beter. Ik heb het aantal 1e rondes naar 50 gebracht en de 2e naar 30.

conv1 .         maxp1 conv2 .        maxp2 conv3 .         maxp3 drop1 dense drop2
32.0 3.0 3.0 2.0 2.0 32.0 3.0 3.0 2.0 2.0 64.0 3.0 3.0 2.0 2.0  0.25   128.0  0.5

17.0 3.0 3.0 2.0 2.0 73.0 2.0 2.0 2.0 2.0 60.0 3.0 3.0 2.0 3.0  0.12   139.0  0.28

Score p [0.04696602795526269, 0.9888]
Score pn [0.02224611087347148, 0.9937]

We scoren nu vergelijkbaar met het 1e, kleinere model. Opvallend weer een terugvoer van de conv1 laag terwijl ook de conv3 laag iets is teruggebracht van 153 naar 139. Ook de dropouts zijn omlaag gegaan. Zou het nog beter kunnen?


Vandaag nog een foutje gecorrigeerd in het model. Hij maakte gebruik niet gebruik van de variabele kansen per ronde maar van de default waardes. Nog maar een keer laten draaien.  

conv1 .         maxp1 conv2 .        maxp2   conv3 .        maxp3 drop1 dense drop2
32.0 3.0 3.0 2.0 2.0 32.0 3.0 3.0 2.0 2.0   64.0 3.0 3.0 2.0 2.0 0.25 128.0 0.5

34.0 1.0 1.0 2.0 1.0 35.0 4.0 4.0 1.0 1.0 124.0 3.0 3.0 1.0 4.0 0.03 345.0 0.5

Score p [0.04698124591551023, 0.9882]
Score pn [0.03509929760087957, 0.9912]

De score is niet echt beter maar er is wel een andere strategie. Alleen de con3 layer is flink groter en de dense layer. Hij heeft ook de eerste conv layer naar 1x1 gebracht. (Ik had voorheen een beperking op minimaal 2x2). De vraag is nu wel of het algoritme een goede kans heeft om de optimale configuratie te vinden of zou die teveel in toevallige keuzes blijven hangen?


conv1 .      maxp1   conv2 .      maxp2   conv3 .      maxp3   drop1 dense drop2
32.0 3.0 3.0 2.0 2.0 32.0 3.0 3.0 2.0 2.0 64.0 3.0 3.0 2.0 2.0 0.25 128.0 0.5
 3.0 1.0 4.0 1.0 1.0 96.0 4.0 2.0 2.0 2.0 39.0 3.0 3.0 1.0 1.0 0.14 152.0 0.34
Score p [0.057666506900917736, 0.986]

Score pn [0.02552617132146238, 0.9932]

Een heel bijzonder resultaat!

maandag 26 februari 2018

Zelf optimaliserende modellen - 1

Naar aanleiding van wat berichten over zelf optimaliserende modellen van Google ben ik zelf eens gaan kijken of ik iets dergelijks zou kunnen bouwen. Uitgaande van de 'beroemde' mnist nummer-dataset (waar geschreven nummers herkent moeten kunnen worden) en een eenvoudig convolutioneel netwerk:

model = Sequential()
model.add(Convolution2D(32, 3, 3, activation='relu', input_shape=(1,28,28)))
model.add(Convolution2D(32, 3, 3, activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.25))

model.add(Dense(10, activation='softmax'))

Ik voeg er wel nog wat Dropout lagen, na de conv lagen, aan toe om het effect mogelijk te vergroten. Uiteraard eerst weer wat worstelen met verouderde code standaarden. (Convolution2D heet nu Conv2D en de input_shape moet andersom - channel last). 

Het idee is om alle aanpasbare parameters random te laten veranderen en daarmee te testen of er beter presterende modellen te vinden zijn.  Omdat een model volledig doorrekenen veel tijd kost ga ik uit van een beperkt aantal 'epochs'. Dat is zeg maar een testronde waarin al het materiaal aan het netwerk wordt aangeboden. Ik kies voor 3 epochs per model. Tevens begin ik met de modellen te testen op een klein deel van de dataset. Ook om de snelheid te verhogen. De mate van 'mutatie' maak ik eerst groot en langzamerhand steeds kleiner.

Ik gebruik deze parameters:


data_part = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7,  0.9,  1.]
rounds    = [30, 20, 8, 7, 6, 5, 4, 3, 3, 3]
change    = [.3, .3, .3, .3, .3, .2, .2, .2, .2, .2]
variance  = [.8, .8, .5, .5, .5, .5, .5, .5, .3, .2, .2, .2]

data_part: geeft aan dat er slechts met een beperkt deel van de beschikbare data wordt getest. Ik maak dit wel 'random' zodat er steeds een nieuw deel van de data wordt gebruikt. Ook van de test data wordt maar een evenredig deel gebruikt. Later heb ik dit voor kleine waardes verdubbeld om toevalstreffers te vermijden. 
rounds: is het aantal testrondes met de gegeven waarden.
change: de kans dat er een verandering plaatsvind
variance: aandeel van de maximale grootte van de verandering  

Na de eerste volledige ronde heb ik deze resultaten:

Score after first full round:
    Score p [0.03745218972601251, 0.9898]
    Score pn [0.02542259713255735, 0.9929]
    
    32.0 3.0 3.0 0.05 32.0 3.0 3.0 0.05 2.0 2.0 0.25 128.0 0.25
  11.0 3.0 2.0 0.04 63.0 4.0 4.0 0.02 4.0 3.0 0.13 119.0 0.2 

Score p - geeft de uitkomsten (validation loss and accuracy) aan van de uitgangspositie. 
Score pn - geeft dat van de nieuw gevonden parameters. 
De parameters geven vooral een verkleining van de eerste conv layer van 32 naar 11 aan en een vergroting van de 2e van 32 naar 63. De poolsize is ook vergroot van 2x2 naar 4x3. De extra dropoutlayers lijken nauwelijks bij te dragen. (van .05 naar .04 en 0.05 naar .02) En lijken dus niet wezenlijk in het model.

Ik test het resultaat nogmaals met een 2e volledige ronde:

Score after second full round:
    Score p [0.027079543936137453, 0.9932]
    Score pn[0.024220995485605818, 0.9937]
    
    11.0 3.0 2.0 0.04  63.0 4.0 4.0 0.02 4.0 3.0 0.13 119.0 0.2
     8.0 4.0 2.0 0.05 111.0 4.0 4.0 0.01 4.0 6.0 0.12 119.0 0.09
Weer iets verbetering maar niet zoveel. Maar met 99,37% wordt het uiteraard steeds moeilijker naarmate je dichter bij de 100% probeert te komen. Het aangepaste model ziet er dus nu zo uit:


model = Sequential()
model.add(Convolution2D(8, 4, 2, activation='relu', input_shape=(1,28,28)))
model.add(Convolution2D(111, 4, 4, activation='relu'))
model.add(MaxPooling2D(pool_size=(4,6)))
model.add(Dropout(0.12))

model.add(Flatten())
model.add(Dense(119, activation='relu'))
model.add(Dropout(0.09))

model.add(Dense(10, activation='softmax'))

Ik heb de extra dropout layers weggelaten wegens het kleine effect. Wat opvalt is een vergroot verschil tussen de eerste en de tweede conv layer: Van 32 naar 8 en de 2e van 32 naar 111. Waarbij de omvang van de convolutie gebiedjes van 3x3 naar 4x3 en 4x4 is verhoogd. Ook de poolsize is stevig opgehoogd van 2x2 naar 4x6 en de oorspronkelijke dropoutlayers zijn verlaagd van .25 naar .12 en .09.
Eens kijken of we op basis hiervan een model kunnen maken dat gebruik maakt van de tendens van deze learnings en wellicht dus nog beter presteert.
Dat lijkt niet eenvoudig. Elke aanpassing geeft een minder resultaat. Het lijkt erop dat het model idd behoorlijk optimaal is binnen het gegeven aantal lagen.
Het blijft verrassen dat de dropout zo wordt teruggeschroefd. Wellicht zorgt de sterk verkleinde eerste convolutie-laag al voor voldoende compensatie voor 'overfitting'. (Waar normaliter drop-out lagen voor worden toegevoegd.)