donderdag 15 oktober 2015

Foute bussen - deel vijf - Neuraal

Het kost weer even moeite om de data 'om te bouwen' voor gebruik in een neuraal netwerk. Ik besluit gebruik te maken van Lasagne omdat het tot zeer overzichtelijke code leidt. Dat kan een chaoot als ik wel gebruiken. :-) 
Onderwater werkt Lasagne met Theano waardoor mijn grafische processor weer mee kan werken. Dat moet snelheidswinst opleveren. Wel moet ik het datatype daarvoor omzetten naar 'float32'. Gek genoeg werkt X = np.asarray(X, type=float32) niet. Gelukkig kom ik er snel achter dat X=X.astype(np.float32) wel werkt. Raar! 
De 'fit' functie van Lasagna werkt al snel. Ik kan de 'predict' functie echter niet zo snel aan de praat krijgen. Het blijkt een oude, bekende fout. Om het juiste inputdatatype te gebruiken moet er een range van 1 groot worden opgegeven. Bijvoorbeeld :
pred = net1.predict(X[si+i:si+i+1]) . Die was ik, volgens mij bij het neuraal bewerken van de Titanic, ook al eens tegengekomen.
Een 2e uitdaging blijkt om de output(hier 'pred') verder te verwerken. Ik krijg bijvoorbeeld dit resultaat: pred = [[ 41.93833542 -87.64888   ]]. Daar ontbreekt een komma! Uiteindelijk vind ik de functie 'tolist()': Die plaatst er gelukkig weer kommas bij. Ik gebruik hem met een verwijzing naar de eerste rij in het array. pred = pred[0].tolist() Dat zorgt voor de verwerking van de dubbele haken. Met lpred, bpred = pred[0], pred[1] kan ik vervolgens de lengte en breedte graad uit elkaar halen.  Draaien maar!


Het eerste wat opvalt is de snelheid! In slechts enkele seconden resultaat. En niet eens zo slecht:         
NN:
count  1329.000000
mean      6.280502
std       3.284298
min       0.057225
25%       3.642903
50%       6.513905
75%       8.674468

max      12.038028

Al iets beter dan de Support Vector Machine en wel, denk ik, 100x zo snel!:
SVM:  
count  443.000000
mean     6.405327
std      3.505544
min      0.067581
25%      3.960455
50%      6.419669
75%      8.952459
max     17.101152

Hoewel ik 400 'epochs' (Een soort minibatches om het NN te leren) heb gekozen omdat het voorbeeld dat aangaf blijkt hij er maar 7 nodig te hebben voor het eindresultaat. Lasagne geeft daar automatisch een mooi rapport over: 

  input             (None, 8)           produces       8 outputs
  hidden            (None, 100)         produces     100 outputs
  output            (None, 2)           produces       2 outputs
  epoch          train loss    valid loss     train/val  dur
-------  ------------------  ------------  ------------  -----
      1  214542254080.00000  515777.40625  415959.00000  0.06s
      2  1944574.12500     354.51895   5485.10645  0.07s
      3     391.01468       0.13764   2840.84082  0.07s
      4       0.07900       0.00129     61.01440  0.07s
      5       0.00122       0.00125      0.98046  0.06s
      6       0.00121       0.00125      0.96734  0.07s
      7       0.00121       0.00125      0.96732  0.07s 
      8       0.00121       0.00125      0.96732  0.07s 

Na 7 epochs leert 'hij' niets meer bij. Hmmmmm ... Nu maar eens lekker aan de parameters sleutelen. 100 hidden layers of 16 blijkt nu niets uit te maken. De grootste winst boek ik door de 'update_learning_rate' sterk te verkleinen van 0.01 naar 0.000001. Dat lijkt intuïtief ook wel logisch omdat de Lenkte- en Breedtegraad natuurlijk feitelijk heel grote getallen zijn in verhouding met de busverplaatsingen. Per leermoment moet er dus niet te grote stappen worden gemaakt.  Nu lijkt hij wel lekker tot de laatste epoch door te leren. Door de snelheid is het leuk om allerlei variaties uit te proberen. Ik zet de hidden units weer op 100. Na 400 epochs is dit het resultaat: 

count  1329.000000
mean      4.286737
std       2.489372
min       0.183120
25%       2.245341
50%       4.071879
75%       6.276527
max      14.031579

Kijk, daar kunnen we al mee thuiskomen :-) Even lekker verder testen. 
Oh ja, de code weer: 

#execfile('/Users/DWW/Documents/Follow1bus outlier neural.py')
import webbrowser
import time
import numpy as np
from sklearn.externals import joblib
from sklearn import svm
import pandas as pd

Data = joblib.load('/Users/DWW/Documents/Bus_outlier.pkl')
Data = np.array(sorted(Data, key = lambda x: (x[0], x[1])))
busses = sorted(list(set([Colm[0] for Colm in Data])))

def dist(est,real):
    if real == 0:
        perc = 0
    else:
        perc = np.absolute(100* (real-est)/real)
    dis = np.absolute(real-est) * 1.852 * 60
    return est, real, perc, dis

def tim(int):
    if len(int) == 7:
        int = '0' + int
    hr = int[0:2]
    mi = int[3:5]
    ds = int[6:8]
    if int[6:8] == 'PM':
        ds = 12
    else:
        ds = 0
    return float(ds) + float(hr) + (float(mi)/100)


X = np.array([0.,0.,0.,0.,0.,0.,0.,0.])
Y = np.array([0.,0.])
t = 0
for i in busses:
    HData = Data[Data[:,0]==i]
    if len(HData) > 5: # Alleen voor bussen waar meer dan 5 waarnemengen van zijn.
        for j in range(len(HData)-4):
            if tim(HData[j+4][1])-tim(HData[j][1]) < 14.: # Alleen de waarnemeningen die inderdaad in tijd bij elkaar liggen. Feitelijk maximaal 8 minuten : 14 genomen voor marge
                X = np.vstack((X,[float(HData[j][2]), float(HData[j+1][2]), float(HData[j+2][2]), float(HData[j+3][2]), float(HData[j][3]), float(HData[j+1][3]), float(HData[j+2][3]), float(HData[j+3][3])]))
                Y = np.vstack((Y,[float(HData[j+4][2]),float(HData[j+4][3])]))
            else:
                t += 1

si = -.1 * len(X)
print 'Aantal records in dataset (X) =', len(X), 'Aantal in testset (si) =', si
X = X.astype(np.float32)
Y = Y.astype(np.float32)

# Voor het neuraal nw maar eens Lasagne proberen.
from lasagne import layers
from lasagne.updates import nesterov_momentum
from nolearn.lasagne import NeuralNet

net1 = NeuralNet(
    layers=[  # three layers: one hidden layer
        ('input', layers.InputLayer),
        ('hidden', layers.DenseLayer),
        ('output', layers.DenseLayer),
        ],
    # layer parameters:
    input_shape=(None, 8),  # 8 input values per batch
    hidden_num_units=100# number of units in hidden layer
    output_nonlinearity=None# output layer uses identity function
    output_num_units=2# 2 target values - lengte , breedte
                 
    # optimization method:
    update=nesterov_momentum,
    update_learning_rate=0.000001,
    update_momentum=0.9,
                 
    regression=True,  # flag to indicate we're dealing with regression problem
    max_epochs=400# we want to train this many epochs
    verbose=1,
    )

net1.fit(X[1:si], Y[1:si])

Tdist = np.array([])
for i in range(len(X[si:])-1):
    pred = net1.predict(X[si+i:si+i+1])
    pred = pred[0].tolist()
    lpred, bpred = pred[0], pred[1]
    l_est, l_real, l_perc, l_dis = dist(lpred, Y[si+i][0])
    b_est, b_real, b_perc, b_dis = dist(bpred, Y[si+i][1])
    Tdist = np.append(Tdist, [l_dis + b_dis])

Td = pd.DataFrame(Tdist)
print(Td.describe())     

Geen opmerkingen:

Een reactie posten