vrijdag 21 augustus 2015

Re-current neural networks

Wow! alsof ik er al wat van begin te begrijpen?! Nou ... iig nog veel te leren. Vandaag een paar keer op de afkorting bptt gestuit. Dat is niet zo gek want ik ben mij wat meer gaan verdiepen in recurrent neural networks. Dit type is in principe meer geschikt voor tijdgerelateerde input zoals bijvoorbeeld video of tekst of geluid. Naast de huidige input krijgt het RNN in elke 'stap' ook de informatie mee van de staat van de vorige 'verborgen neurons'. Dit geeft het een soort geheugen waarmee het uiteindelijk goede tijdvoorspellingen kan doen.


Het RNN lijkt mij een uitstekend model voor een heleboel toepassingen. Met name ook om 'afwijkend gedrag' te kunnen constateren. (Anomaly detection). Misschien toepassen op mijn familie? :-)

Het eerste, eenvoudige RNN kopieer ik van "Implementing a recurrent neural network in python
Het testmodel 'voorspelt' het product van 2 getallen. Mmmm .... Na 500.000 !!! leerloops krijg ik dit resultaat:


Oke, blijkbaar doet ie het. De kostfunctie dendert omlaag. Maar snappen (toepasbaar maken) doe ik het nog niet.  Misschien ook deze enthousiaste uitleg maar eens doornemen. Voorspellen van letters en uiteindelijk hele Shakespeare teksten genereren! 


Oh ja, en BPTT staat voor back propagation trough time :-) 

woensdag 19 augustus 2015

Lasagne

Lasagne. Nee, niet het Italiaanse pasta gerecht. Maar weer een aanvullende manier om het werken met neurale netwerken eenvoudiger te maken.

Ik volg voor de aardigheid de tutorial van Daniel Nouri over de Kaggle challenge van het herkennen van gezichtskenmerken: http://danielnouri.org/notes/2014/12/17/using-convolutional-neural-nets-to-detect-facial-keypoints-tutorial/

Daarin wordt dus naast Python en Numpy, ook Pandas en dus Lasagne gebruikt. Eerst maar eens kijken of we de voorbeelden kunnen naspelen.

Lasagne werkt feitelijk meer met parameters in plaats van programmacode. Hoewel er daarmee veel onder de motorkap verdwijnt (maar dus ook verzorgd wordt) geef je dus met parameters op welke code (methodieken) er gebruikt met gaan worden. Bijvoorbeeld:

net1 = NeuralNet(
    layers=[  # three layers: one hidden layer
        ('input', layers.InputLayer),
        ('hidden', layers.DenseLayer),
        ('output', layers.DenseLayer),
                         ],
    # layer parameters:
    input_shape=(None, 9216),  # 96x96 input pixels per batch
    hidden_num_units=100# number of units in hidden layer
    output_nonlinearity=None# output layer uses identity function
    output_num_units=30# 30 target values 
... 

Na wat experimenteren lukt het me om alles te laten lopen. Kijken of we de rest van de tutorial ook kunnen naspelen om daarna, uiteraard, de resultaten ruimschoots te overbluffen :-)

Wat zou die met die waarschuwing bedoelen ... ?

/Users/DWW/src/lasagne/lasagne/init.py:86: UserWarning: The uniform initializer no longer uses Glorot et al.'s approach to determine the bounds, but defaults to the range (-0.01, 0.01) instead. Please use the new GlorotUniform initializer to get the old behavior. GlorotUniform is now the default for all layers.

  warnings.warn("The uniform initializer no longer uses Glorot et al.'s "


zondag 9 augustus 2015

Nvidea Deep Learning Courses

Dat Nvidea enthousiast is over de deep learning ontwikkelingen op GPU's die in 2012 is ingezet, mag geen verrassing zijn.
Usage of GPU's in yearly ImageNet competition

Inmiddels organiseren ze (jaarlijks?) conferenties hierover en brengen ze aanvullende, specialistische software hiervoor uit. Naast Cuda, de standaard C++ ontwikkel omgeving voor GPU applicaties hebben ze nu ook CuDNN en Digits uitgebracht. CuDNN levert supersnelle, geoptimaliseerde functies voor gebruik in (convolutional) neurale netwerken en met Digits is e.e.a. heel makkelijk grafisch te volgen. (Hoop dat ik het goed samenvat)



Daarnaast organiseert Nvidea een veelbelovende DL cursus waarbij nauwelijks voorkennis over DL's vereist lijkt te zijn. Hij loopt al en de eerste videos zijn al terug te vinden:    

https://developer.nvidia.com/deep-learning-courses


Bingo!

Gelukkig, ook het neurale netwerk werkt nu met de Titanic data! Het probleem bleek een te trage leercurve te zijn. Zodra ik de 'leer quotient' die aangeeft welk aandeel van de gradient moet worden genomen om de 'weights' aan te passen ophoog van 0.01 in de richting van bijvoorbeeld 0.3, begint de bereikte betrouwbaarheid omhoog te gaan. Afhankelijk van enkele andere parameters tot wel 82%.


Als ik de standaard stogastic gradient funktie (sgd) vervang door de RMSprop (uit het 'modern net' - zie eerdere berichten) functie lijkt e.e.a. nog beter te gaan. (tot boven de 84%) Wel blijft het resultaat vrij sterk afhankelijk van de voorsortering (shuffling) van de gegevens. Waarschijnlijk is de dataset te klein (750 voorbeelden en 141 testwaardes) om dit te voorkomen.

Ik heb de hidden laag in grootte aangepast tussen 0.5 * Col (0.5 * 8 inputkolommen = 4) en 10 * Col. Het effect blijft slecht zichtbaar. Rond 8 hidden neurons lijkt een soort optimum. De initiële random invulling van de netwerk 'weights' geeft zo-ie-zo variatie in de uitkomsten.

Hieronder de gebruikte code. Ik heb de voorbewerkingen van de data (klasse variabelen met per waarde een kolom en standaardiseren van numerieke waarden tussen 0 en 1) in een apart programma gezet.


'''
    File format
    0    sequence number To compare with original data
    1    age             Age (between 0 and 1)
    2    sibsp           Number of Siblings/Spouses Aboard (between 0 and 1)
    3    parch           Number of Parents/Children Aboard (between 0 and 1)
    4    survival        (0 = No; 1 = Yes)
    5    no survival     (0 = No; 1 = Yes)
    6    pclass1         Passenger Class (0 = No; 1 = Yes)
    7    pclass2         Passenger Class (0 = No; 1 = Yes)
    8    pclass3         Passenger Class (0 = No; 1 = Yes)
    9    sexFemale       Sex (0 = No; 1 = Yes)
   10    sexMale         Sex (0 = No; 1 = Yes)
'''
import csv
import numpy as np
from sklearn.utils import shuffle

# File inlezen
file = '/Users/DWW/Downloads/train_out.csv'
with open(file,'rb') as f:
    reader = csv.reader(f)
    Data = list(reader)

x_cols = [1,2,3,6,7,8,9,10]             # De kolommen met relevante invoer waarden
y_cols = [4,5]                          # De uitkomst kolommen (survival / no survival)

DataN = np.array(Data,dtype=np.float)   # Volledig overzetten van Python naar Numpy
Rec, Col = DataN.shape

XX = DataN[:,x_cols]                     # Alle waarden
YY = DataN[:,y_cols]                     # Alle uitkomsten

XX, YY = shuffle(XX,YY , random_state=1) #, random_state=1

Size = 750
x,y   = XX[:Size], YY[:Size]        # Oefendeel
xt,yt = XX[Size:], YY[Size:]        # Testdeel

Rec, Col = x.shape
print x.shape, xt.shape
#-----------------------------------------------

import theano
from theano import tensor as T
import numpy as np

def floatX(X):
    return np.asarray(X, dtype=theano.config.floatX)

def init_weights(shape):
    return theano.shared(floatX(np.random.randn(*shape) * 0.01))

def RMSprop(cost, params, lr=0.01, rho=0.9, epsilon=1e-6):
    grads = T.grad(cost=cost, wrt=params)
    updates = []
    for p, g in zip(params, grads):
        acc = theano.shared(p.get_value() * 0.)
        acc_new = rho * acc + (1 - rho) * g ** 2
        gradient_scaling = T.sqrt(acc_new + epsilon)
        g = g / gradient_scaling
        updates.append((acc, acc_new))
        updates.append((p, p - lr * g))
    return updates

def model(X, w_h, w_o):
    h = T.nnet.sigmoid(T.dot(X, w_h))
    pyx = T.nnet.softmax(T.dot(h, w_o))
    return pyx


X = T.fmatrix()
Y = T.fmatrix()

w_h = init_weights((Col, Col * .2 ))
w_o = init_weights((Col * .2, 2))

py_x = model(X, w_h, w_o)
y_x = T.argmax(py_x, axis=1)

cost = T.mean(T.nnet.categorical_crossentropy(py_x, Y))
params = [w_h, w_o]
updates = RMSprop(cost, params)

train = theano.function(inputs=[X, Y], outputs=cost, updates=updates, allow_input_downcast=True)
predict = theano.function(inputs=[X], outputs=y_x, allow_input_downcast=True)

for i in range(10000):
    cost = train(x,y)

print 'Reliability: ', np.mean(np.argmax(yt, axis=1) == predict(xt))

# Print enkele voorbeelden
for i in range(10):

    print '>>',np.argmax(yt[i]) , predict(xt[i:i+1])

zaterdag 8 augustus 2015

Foutmelding opgelost - probleem blijft.

Ook de mnist dataset blijkt niet per 'record' met de predict functie aan te roepen. Blijkbaar verliest die de essentiële array-structuur zodra er 1 regel van wordt geselecteerd. De oplossing blijkt simpel. Gewoon een 'range' van 1 groot opgeven:

for i in range(10):

    print '>>',np.argmax(yt[i]) , predict(xt[i:i+1])

Het blijkt dat voor elke testwaarde dezelfde index [1] (verdronken) wordt teruggegeven. Op een of andere wijze leiden de 'weights' (nog) niet naar het alternatief. Dat je ook wel eens gered zou kunnen worden.

hmmm


vrijdag 7 augustus 2015

Een vastlopertje

Na de Titanic experimentjes met scikit's svm (support vector machine) nu eens een echt neuraal netwerkje aan de Titanic gegevens koppelen. Ik kies hiervoor eerst het 'standaard net' uit de eerdere Python / Theano cursus. Als input gebruik ik dan dus niet de cijferafbeeldingen van de mnist dataset maar de Titanic passagiers gegevens.
Het 'high-end' convolutional netwerk is hiervoor minder geschikt omdat dat meer een relatie legt tussen de verschillende pixellocaties in een afbeelding. Deze ontbreken uiteraard in de Titanic set.

Het eenvoudiger netwerk heeft 1 inputlaag, 1 verborgen laag en 1 output laag. De dimensies moeten natuurlijk worden aangepast. Input maken we gelijk aan het aantal kolommen dat we uit de dataset aan gaan bieden. De output is 2 groot (survived of niet => to be or not to be). Het formaat van de tussenlaag (of lagen) in een NN blijkt een kwestie van gevoel. Voorlopig maar eens 2x het aantal colommen.

Het is even worstelen om weer alles op de juiste manier, met de juiste dimensies, aan te bieden. Het resultaat blijft echter teleurstellend. Het rekent snel maar wat ik ook verander de uitkomst is ongeveer 63 %. Zelf als ik het NN niet laat leren. Er zit dus ergens een fout. 2 avonden later ben ik nog niet veel wijzer. Alleen de code om de betrouwbaarheid uit te rekenen geeft misschien een aanwijzing.

Het aanbieden van 1 dataregel uit de testdataset aan de 'predict-functie' geeft een foutmelding:

TypeError: ('Bad input argument to theano function with name "/Users/DWW/Documents/3_net_titanic.py:155"  at index 0(0-based)', 'Wrong number of dimensions: expected 2, got 1 with shape (8,).')       

Dat lukte met de mnist dataset volgens mij wel. Maar weer verder zoeken.


zondag 2 augustus 2015

Gelukkig, terug van vakantie :-)

Na een heerlijke zeilvakantie in Griekenland, zonder Euro of Drachmen problemen, eindelijk weer wat DL hobbyen. Allerlei nieuwe ideeën opgedaan. Eerst maar weer eens de draad met de Titanic zien op te pakken. Hoewel de eerste resultaten aardig leken zijn er wat verkeerde uitgangspunten. Zo blijkt het beter om de 'klasse variabelen', zoals de sekse en de passagiers klasse in aparte 'kolommen te zetten per klasse'. Dus een man krijgt dan een '1' in de kolom 'man' en een '0' in een aparte kolom 'vrouw'. Een vrouw uiteraard precies omgekeerd. Dit voorkomt ondermeer dat er een soort continuïteit wordt gesuggereerd die er niet is.

Oei, dan moet ik extra kolommetjes bijmaken en de oorspronkelijke daarna verwijderen. En het liefst allemaal 'dynamisch'. Zonder dat ik hoef op te geven hoeveel verschillende waardes de klasse variabelen bevatten. Na wat sleutelen voeg ik deze code toe:

# New class processing: Every class gets its own column
for j in range(0, Col):
    if j in class_cols:
        c = sorted(list(set([Colm[j] for Colm in Data]))) # c = list met de verschillende klassen.
        l = len(c)                                        # l = aantal klassen
        h = np.zeros((Rec, l))                            # h = hulp matrix
        print 'Colom :',j, ' heeft records, classes : ', h.shape
        for i in range(0, Rec):
            h[i,c.index(Data[i][j])] = 1
        DataN = np.hstack((DataN,h))                      # 'h' achteraan plakken

De betrouwbaarheid lijkt echter iets achteruit te gaan.  
('gamma, C = ', 0.8, 1000, 'Prediction : ', 0.84615384615384615)

Oorspronkelijk kwam ik zelfs op 
('gamma, C = ', 0.8, 1000, 'Prediction : ', 0.8571428571428571)

mmmm.

Ook veranderen de resultaten vrij veel als ik de dataset eerst door elkaar schud. Daar moet ik nogeens wat dieper in duiken. 

Ik probeer daarnaast opnieuw of de passenger class toch geen informatie toevoegt. Volgens wat analyses op Kaggle zou dat namelijk wel het geval zijn. Het lijkt nog steeds tot slechtere resultaten t e leiden. Misschien heeft e.e.a. te maken met de verkeerde processing van lege waarden? Ga ik een andere keer weer eens naar kijken.