zondag 29 november 2015

LSTMs - met James Potter

Ok, wie is James Potter en wat heeft hij met LSTMs te maken? Wel, het is natuurlijk leuk om automatisch teksten van Nietzsche te generen maar om een beetje te begrijpen hoe 'goed' die het doet is Nietzsche misschien toch wat te ingewikkeld. Maar eens even gezocht naar een beschikbare Nederlandse tekst: "James Potter en de vloek van de poortwachter". Een boek van G. Norman Lippert dat gebaseerd is op de karakters en werelden van J.K. Rowling. Je weet wel, die van Harry Potter.

Het blijkt inderdaad niet moeilijk om deze tekst aan het LSTM te voeden. Wel eerst even naar 'plain text' omzetten om de opmaak en de plaatjes kwijt te raken en dan laden maar.

De tekst bestaat uit 1.180.397 karakters (65 verschillende) en levert 'dus' 393.459 sequences op.

Het duurt toch lang voordat de 60 iteraties zijn uitgevoerd. Per iteratie ongeveer 9 minuten. Dus even tijd om na te denken en de Keras documentatie wat verder door te lezen.

Wat mij nog ontgaat is hoe de volgordelijkheid in dit algoritme wordt meegenomen. Natuurlijk, de letters volgen elkaar op maar de bieden we in feite in 20 letters per keer aan en de 21e moet 'hij' dan voorspellen. Dat er een volgorde in die 20 letters ziet het netwerk feitelijk niet. De 19e letter is even belangrijk als de 20e en dus even belangrijk als bijvoorbeeld de 1e. Dat lijkt toch 'tegen intuïtief' ?! De letters dichtbij de te voorspellen letter zouden toch 'belangrijker' moeten zijn?

De vraag is dan ook of een 'gewoon' neuraal netwerk dit zo niet even goed zou kunnen doen. Er worden dan ook outputs berekend op basis van 'een reeks' inputs. (features genoemd) Zoek de verschillen?! Maar wellicht zie ik een stukje logica over het hoofd.

Wat leuker is om te bedenken is of er, in plaats van letter generatie, ook met woorden generatie gewerkt kan worden. Zou dat geen nog effectievere teksten opleveren? De uitdaging is dan wel dat er zowel aan input zijde als aan output zijde veel grotere vectoren ontstaan. Even groot als het aantal gebruikte woorden in de betreffende tekst. Maar misschien kunnen we (en moeten we) het formaat limiteren tot bijvoorbeeld de 1000 meestgebruikte woorden. Eens kijken of we dit voor elkaar kunnen krijgen.

De eerste resultaten geven 241.236 woorden. Er zijn 14.069 unieke woorden! Toch wel veel. Zou dat lukken?


Het eerste programma heeft ondertussen doorgewerkt op de LSTM met karakters. Bij iteratie 24 stop het programma met een of andere memory error. Mogelijk omdat het 2e programma (LSTM met woorden) ook al de nodige capaciteit heeft gevraagd. 

"Error allocating 5200 bytes of device memory (unknown error). Driver report 4003065856 bytes free and 4294770688 bytes total "

Gelukkig geeft hij tussendoor resultaten. Het begint toch al ergens op te lijken. Straks een nieuw boek uitgeven? :-)

--------------------------------------------------
Iteration 23
Epoch 1/1
393459/393459 [==============================] - 464s - loss: 0.9507     

----- diversity: 0.2
----- Generating with seed: "ereen die had meeged"
ereen die had meegedaan? zelfs jullie zijn dat je me herinneren dat hij het ook zij, alsof hij had gezien in een van de standbeeld van de deur en stapte op de trein.
‘wat?’ zei james zijn hand op. ‘ik heb nog niet kon zien. hij keek naar james op het totel behoorlijk gegeven door een stekelijke verdering om te bezichten te zijn om hem te verschenen tot hij de deur van de deur van de stok van de deur van de deur
----- diversity: 0.5
----- Generating with seed: "ereen die had meeged"
ereen die had meegede leeg. hij perste zijn stem op zijn hoofd naar haar. en ze had de andere kant van de aansluiterde kon de hand tegen de vloek van de poortwachter tegen de donkere voeten onder de vloer van de droom van een verhaal zien. ze heeft zijn hand en stond omhoog de deur van de voorstelling van de hartstelling van zweinstein, als de leste gesprek hem open en james had geluk in zijn hand en zag hem en begon
----- diversity: 1.0
----- Generating with seed: "ereen die had meeged"
ereen die had meegede leerden. het p?dat west. ik heb geen zie aan het pad om deze langei? van vervangen spiegel door de kluis niet ermie magie,’ zei het soort van zweinstein.’
‘laten we komen ze genoeg was. james besloot om hem te zueren. ik had gezicht van ons laat.een. hoe was het??2 het lang ik doe, natuurlijk niet langen waren. ‘en we samen een dolk was ver wacht toen ik hij niet verdien tussen haar blik
----- diversity: 1.2
----- Generating with seed: "ereen die had meeged"
ereen die had meegedaan?.’
een streep vloer langs randpen rusfening.
‘ankazekmezing was om te leggen, ol ze voor iets wat treis hij invererde dat je stopte hhabden om te gereiste ;ogevong. en bereikte, een laatste belondig liet zalazar rechtschieven we op ginat de trein als1. zalazar?’verdeerde de stem telug. ze zei dat ze erg nooi mee als daarom.’
765

harry nam laatheer. het valt morgen gegaan dat. hojjs roo


LSTMs met Nietzsche

Long Short Time Memory is een meer uitontwikkelde vorm van RNN (Recurrent Neural Network) en met name bruikbaar voor tijd of 'volgordelijkheid' gerelateerde informatie. Een aardig voorbeeld is het automatisch genereren van nieuwe tekst op basis van bestaande teksten. Dit is o.a. beschreven in Andrej Karpathy's blog 'The unreasonable effectiveness of recurrent neural networks'.  

In mijn vorig blog bericht heb ik een Keras voorbeeld hiermee getest dat werkt op basis van teksten van Nietzsche. De sourcecode heb ik hier gevonden.


In hoofdlijnen lijkt het als volgt te werken.

De tekst van Nietzsche wordt binnengelezen. (Kan overigens elke tekst zijn. Aanbevolen is minimaal 100k aan karakters maar beter is een miljoen of meer - Deze is 600901 karakters lang)

Er wordt gekeken hoeveel soorten karakters in de tekst zitten. Deze krijgen een indexcijfer. In dit geval zijn dat er 59.

De tekst wordt opgesplitst in overlappende 'regels' van 20 karakters. Ze noemen ze hier 'sequences'. Uiteindelijk worden dat er 200294 doordat er een stapgrootte van 3 tekens wordt gekozen. (Dus steeds 17 tekens overlappend)

In het proces 'Vectorization' wordt een tabel van boolean waardes opgebouwd uit de bovengenoemde sequences.  Die moet er ongeveer zo uitzien:

0  1  2  3  4  5  ...  20 Sequence lengte
0  0  0  0  1  0  ...   0
1  0  0  0  0  1  ...   0
2  0  1  0  0  0  ...   0
3  0  0  0  0  0  ...   0
...
59 Karakter index

Deze vector is dan 200294 lang in ons voorbeeld en vormt uiteindelijk de 'te trainen X input'
De Y (Hier als kleine y geschreven. Conventie?) moet het 'eerst volgende karakter' gaan weergeven. Deze wordt ook in een vector van 59 booleans opgeslagen.

Nieuw voor mij is het hier veel gebruikte 'enumerate' commando dat een volgnummer en de waarde als resultaat geeft. Bijvoorbeeld

for x,y in  enumerate( ['a','b','d']):
     print x,y
0 a
1 b
2 d  

Handig want dan hoef er geen aparte 'index-variable' te worden bijgehouden.

Hierna wordt het Keras model gedefinieerd en gecompileerd:


print('Build model...')
model = Sequential()
model.add(LSTM(512, return_sequences=True, input_shape=(maxlen, len(chars))))
model.add(Dropout(0.2))
model.add(LSTM(512, return_sequences=False))
model.add(Dropout(0.2))
model.add(Dense(len(chars)))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

Input_shape is hier dus 20 x 59 en de uitvoer (onderste 'Dense-layer') is 59 groot. Daartussen zitten 2 LSTM layers en twee Dropout layers. De dropout geeft 'ruis' tijdens het 'leren' zodat het netwerk robuuster wordt.

In het laatste deel van het programma wordt het netwerk 'geleerd' en tussendoor worden test-resultaten afgedrukt.

model.fit(X, y, batch_size=128, nb_epoch=1)

De nieuw te genereren tekst wordt gestart op basis van een random stukje tekst (20 karakters) uit de oorspronkelijke tekst van Nietzsche. ('the seed') Bijvoorbeeld (inclusief end-of-lines) :

"n him in
order to pr"

De code hiervoor is:

preds = model.predict(x, verbose=0)[0]

De [0] lijkt aan te geven dat er meer dan een karakter wordt voorspeld. Misschien later eens kijken of dat waar is. 

Er is ook een 'sample' functie gedefinieerd met een variabele 'diversity' of 'temperature'. Ik snap nog niet goed hoe die werkt maar het doel is meer of minder variatie (creativiteit? :-) in de uitvoerteksten te krijgen.

Een voorbeeld resultaat staat al in mijn vorig bericht.

Technisch aardigheidje om te onthouden hier is ook het gebruik van de sys.stdout.write in plaats van print.  







zaterdag 21 november 2015

Blijven Bazelen ? Of naar Keras over?

Helaas, de bazel (niet basel - waar het icoontje van lijkt afgeleid) installatie lukt nog niet. Er moet een bepaalde Workspace worden ingericht met een (lege) WORKSPACE file en een BUILD file en java directories ... Ik kom er helaas nog niet uit. Daar gaat mijn Tensorflow LSTM testje. Helaas;

no such package '': BUILD file not found on package path.

Misschien later nog eens proberen.

Een ander interessant platform (voor LSTMs) lijkt Keras. De eerste voorbeeldcode ziet er wel erg eenvoudig uit:

from keras.models import Sequential from keras.layers.core import Dense, Dropout, Activation from keras.layers.embeddings import Embedding from keras.layers.recurrent import LSTM model = Sequential() model.add(Embedding(max_features, 256, input_length=maxlen)) model.add(LSTM(output_dim=128, activation='sigmoid', inner_activation='hard_sigmoid')) model.add(Dropout(0.5)) model.add(Dense(1)) model.add(Activation('sigmoid')) model.compile(loss='binary_crossentropy', optimizer='rmsprop') model.fit(X_train, Y_train, batch_size=16, nb_epoch=10score = model.evaluate(X_test, Y_test, batch_size=16)

Kijk daar kan ik misschien zelfs nog wel uitkomen. :-) Keras is (ook) gebaseerd op Theano en gebruikt (dus?) ook de mogelijkheden van de GPU. Met name voor LSTM's is dat blijkbaar ook wel heel erg zinvol. De installatie loopt op zich goed maar het pakket heeft ook wat andere afhankelijkheden. Numpy, Scipy, Pyyaml, Theano (optioneel ook HDF5 en h5py?? en cuDNN voor cnn's - convolutional neural networks) .
Van de 'verplichte afhankelijkheden had ik Pyyaml volgens mij nog niet.   "What It Is: YAML is a human friendly data serialization standard for all programming languages." Zelf vraagt het ook weer om libYaml, uit dezelfde bron. Ok, hij geeft wat warnings maar laten we maar eens kijken hoever we komen.

Uiteraard blijkt bovenstaande Keras code niet afdoende.  Al snel vraagt hij om 'max_features', een onbekende variabele. Ook de andere zijn niet gedefinieerd evenmin als de 'leerdata' X_train, Y_train) Ik zoek op internet naar een compleet voorbeeld en vindt een tekstgenerator op basis van teksten van Nietzsche.    In 'no time', nou ja ... na een lange tijd begint 'hij' bij elke iteratie 'voorbeeldteksten' te genereren. Leuk! Hij doet het! Nu nog 'even begrijpen' en dan mijn eigen data hopelijk los gaan laten.

...
--------------------------------------------------
Iteration 21
Epoch 1/1
200294/200294 [==============================] - 234s - loss: 1.0117     

----- diversity: 0.2
----- Generating with seed: "see what
these thing"
see what
these things with the same power, the most serve as the most servents of the sentiment of the same with a profoundest and desires and
distrustful and as a morality of all the present because of the present to a present that the strict serse of the senses, something of the soul who has the counterpreted and presents the conscience of the same something of the self-subtical strongte, the most disglist of the s
----- diversity: 0.5
----- Generating with seed: "see what
these thing"
see what
these things with a genius to the
self-subjection, his result, the desires of such a moral favility of the subject of every you and all the case of the higher and his his fauth as the conscience of say and one seems to memory the same conscience of religions which is the primordial sanntaitioms undifferent and surpossition, so that the influence of the present to a proposition of the soul, who has the way to
----- diversity: 1.0
----- Generating with seed: "see what
these thing"

see what ...

vrijdag 13 november 2015

Bazel-en

Mooi, de eerste Tensorflow werkt. Met het plaatje met de 'wordcloud' om het te bewijzen. Goed, nog niet echt als nieuwe toepassing maar wellicht later nog eens goed te gebruiken. De tutorials geven ook een aanwijzing voor een recursief neuraal netwerk, of beter, een LSTM netwerk! Een lstm (long short term memory) is een specifiek recursief netwerk dat een 'langer geheugen' heeft. Er is een mooie beschrijving bij geleverd maar het ziet er complex uit. Gelukkig lijkt de code 'iets' eenvoudiger. Alleen moet er voor gebruik eerst van ales bij geïnstalleerd worden. Eerst de laatste versie van JDK (Java development kit - dat blijkt vrij eenvoudig) en daarna 'Bazel'. Hmm ... dat is toch hopelijk niet in het Nederlands bedoeld. Het blijkt een Beta versie van Googles eigen 'software builder'. Hier staat het beter beschreven.


Bazel is beschikbaar voor Unix en voor Max OS. Ik probeer de installatie instructie te volgen maar deze is voor een beetje 'pro's' geschreven. Eerste de juiste OS versie selecteren. Mac OS staat er niet bij. Na enig naspeurwerk blijkt dat ik bij 'Darwin' moet zijn. De oorspronkelijke 'core' van het huidige operating system.
Ik selecteer bazel-0.1.1-installer-darwin-x86_64.sh
Nu nog proberen hem aan de praat te krijgen. Het lukt uiteindelijk om deze instructies:

$ chmod +x install-version-os.sh
$ ./install-version-os.sh --user
Te vertalen naar

$ chmod +x /Users/DWW/Downloads/bazel-0.1.1-installer-darwin-x86_64.sh
$ /Users/DWW/Downloads/bazel-0.1.1-installer-darwin-x86_64.sh --user
Braaf geeft hij aan: Bazel is now installed!

Make sure you have "/Users/DWW/bin" in your path. You can also activate bash
completion by adding the following line to your ~/.bash_profile:
  source /Users/DWW/.bazel/bin/bazel-complete.bash

Nu nog even de $HOME/bin toevoegen:


PATH="$PATH:$HOME/bin"

en hopen dat ie het doet!

donderdag 12 november 2015

Tensorflow Word2Vec

Het heeft even geduurd. Eerst een hele nacht. Nogmaals opgestart met een extra printlijn want de cursor bleef wel heel erg stil. Weer een dag. Hielp ook niet. Blijken de 'indents' van Python zich toch weer gewroken te hebben.  Een foutje - geen idee wat hij wel aan het doen was. Daarna was alles heel langzaam. Toch maar even herstart. Even kijken hoe ik Tensorflow dan ook alweer moest activeren. Oh ja, eerst op de commandlijn:

source bin/activate

en daarna pas Python opstarten.
Ja, hij begint nu een enorme hoeveelheid nullen en enen uit te spuwen. Misschien niet zo'n goed gekozen printcommando. Maar hij lijkt nu verder te komen. Ineens print hij dit uit en stopt daarna:

.
.
1
0
1
Average loss at step  100000 :  4.67785296774
Nearest to a: the, any, another, naaman, tamarin, perth, agouti, thaler,
Nearest to nine: eight, seven, six, five, four, zero, microcebus, callithrix,
Nearest to be: been, have, refer, is, were, are, by, was,
Nearest to however: but, that, and, abitibi, although, reuptake, cegep, which,
Nearest to by: was, be, menthol, abitibi, with, were, preclear, after,
Nearest to three: five, four, two, six, seven, eight, tamarin, nine,
Nearest to d: b, microsite, thracians, pmid, daddy, m, gland, stereochemistry,
Nearest to which: that, this, what, thaler, it, also, but, and,
Nearest to when: if, where, after, during, although, while, agouti, wct,
Nearest to had: has, have, was, were, insurgency, microsite, altenberg, abitibi,
Nearest to some: many, these, several, all, the, other, their, those,
Nearest to but: however, and, trinomial, cegep, agouti, abitibi, although, while,
Nearest to UNK: dasyprocta, cegep, tamarin, abitibi, callithrix, akita, agouti, thaler,
Nearest to also: never, now, which, often, thaler, still, sometimes, who,
Nearest to after: before, when, during, in, from, cegep, cebus, at,
Nearest to into: through, with, from, back, during, roshan, to, under,

>>> 

Hmm lijkt wel ergens op maar toch had ik nog iets meer verwacht. Wel gelukkig redelijk snel. Zelfs zonder gpu ondersteuning. Eens even de code doorspitten. Aha, hij heeft het verwachte resultaat in een bestandje geplaatst. Mijn eerste Tensorflow en eerste word2vec programma is een feit! Ok, nog geen eigen code maar toch 'count your blessings' ...  Snik ... ontroerend ...


woensdag 11 november 2015

Tensorflow

Weer een nieuw DL platform ? Na Numpy/Theano, Lasagne, R, PyBrain, CuDNN etc. is er nu ook Tensorflow beschikbaar. Maar ja, deze is van Google zelf! Een van de koplopers in deep learning! Dan de diagnostiek maar even aan de kant! Dat moet ik uitproberen! De installatie lijkt redelijk gemakkelijk. Wel lees ik dat er voor Mac nog geen gpu ondersteuning is. Jammer! Ook ziet hij niet dat ik heeeel veel processoren heb (2x quad core):

>>> sess = tf.Session()

can't determine number of CPU cores: assuming 4

Er blijkt echter ook sterk geïnvesteerd te zijn in tutorials. Kijk, daar hou ik van! Na wat klein spielerei probeer ik een voorbeeld van een conv2vec applicatie. Ook leuk om daar de theorie eens omheen te lezen. Hierbij worden woorden in een multidimensionale ruimte gerangschikt naar hun vergelijkbare posities (gebruik) in een zin.   Of, zoals ze het daar beschrijven:
"Vector space models (VSMs) represent (embed) words in a continuous vector space where semantically similar words are mapped to nearby points"

De code is bijgeleverd inclusief het ophalen van een groot voorbeeld tekstbestand. (17.005.207 woorden). Het kopiëren hiervan in de Xcode editor gaat lastig. Python is erg gevoelig voor 'indents' (inspringingen - wat een woord) en de kopieer slag maakt er een 'zooitje van'. Maar goed na enig correctiewerk lijkt het programma weer op het voorbeeld. Lopen maar! Nu wreekt zich het ontbreken van gpu ondersteuning. Na wat eerste printregels zoals deze hieronder blijft de cursor doodstil. Blijkbaar wat geduld nodig. Het is nu woensdag 21:37.  Ik ben benieuwd!
Found and verified text8.zip
Data size 17005207
Most common words (+UNK) [['UNK', 418391], ('the', 1061396), ('of', 593677), ('and', 416629), ('one', 411764)]

Sample data [5239, 3084, 12, 6, 195, 2, 3137, 46, 59, 156]
    

zondag 8 november 2015

Diagnostiek - deel 5

Eventjes een uitstap gemaakt naar PyBrain. Ook weer een 'eenvoudiger manier' om neurale netwerken te bouwen. Met name interessant omdat ik mij in re-current (je weet wel - die een beetje 'tijdgevoel' hebben) wil verdiepen. Er is een redelijk eenvoudig voorbeeld beschikbaar en algemeen lijkt het een heel bruikbaar platform. Alleen denk ik niet dat het op basis van Theano werkt (zoals bijvoorbeeld Lasagne) waardoor de grafische processor niet wordt ingezet. Maar ... misschien moet ik nog wat verder experimenteren.

Bij de eerdere diagnostische data zit geen echte classificatie. Een superviced netwerk training wordt daarmee dus lastig. Wel zit er een geschreven toelichting en een conclusie bij. Daarin wordt regelmatig vergelijkbare terminologie gebruikt zoals 'ernstig' en 'fors'. Het moet dus mogelijk zijn om een classificatie af te leiden. Enerzijds probeer te classificeren tussen wat 'ernstig is en wat niet. Anderzijds probeer ik een meer gedetailleerde classificatie te maken op waar het 'ernstig' op slaat.


Ik selecteer op de woorden:

slecht = ['slecht','slechte', 'fors', 'forse', 'ernstig', 'ernstige', 'abnormaal', 'abnormale', 'gigantisch', 'gigantische', 'massale', 'massaal', 'dramatisch','dramatische', 'enorm', 'enorme']

De 'negatie'  daarvan test ik ook:


neg = ['niet', 'geen' , 'enig', 'enige', 'wat', 'iets', 'wat', 'geringe', 'matige']

Soms hebben de 'slecht-woorden' betrekking op de beeld of meting kwaliteit. Ik haal die er zoveel mogelijk uit.


meting = ['afgrensbaar', 'opneembaar','waarneembaar','te', 'echovenster', 'echodens', 'beeld', 'patient', '-', 'echobeeld', 'beeldkwaliteit', 'echovensters']

En daarna probeer ik het woord voor of na als detail klasse te selecteren. Uiteraard zitten daar door de verschillende schrijfwijzen veel missers in. Eerst nu maar eens kijken of ik de hoofdclassificatie ernstig of niet kan aanleren. Met een eenvoudig neuraal netwerk op basis van Lasagne kom ik uit op ongeveer 80% betrouwbaarheid. Hoewel er altijd gestoei is met de programmafouten merk ik wel dat ik meer routine begin te krijgen.
   



      

woensdag 4 november 2015

Diagnostiek - deel 4

Wow! Het outlier (of anomaly) rapportage programma lijkt in een keer goed te werken! Alleen worstel ik nog even om niet alle parameters uit de 'contributie lijst' af te drukken. Alleen de 10 belangrijkste. Terug naar een eenvoudig loopje en dat werkt ook weer. Het lijkt tot heel veel belovende resultaten te leiden. Zelfs de voorhanden zijnde specialist begint nu enthousiast te worden. Of iig enthousiaster. :-) Het resultaat laat bijvoorbeeld op deze wijze zien wat de procentuele bijdrage in de afwijking (van het gemiddelde) per parameter is:

Grootste bijdrage (%):
LVEDV(MOD A2C)       6.78345076599
LVEDV(MOD BP)        6.68302073818
LVESV(MOD A2C)       6.09048502588
SV(MOD A2C)          6.03219066699
LVESV(MOD BP)        6.0074237466
MM/LVIDs             3.71322745138
LAAs(A2C)            3.64484067288
MM/LVIDd             3.62173487804
LAESV(MOD A2C)       3.40709766584
LVEDV(MOD A4C)       3.33654947276  

Door de beschreven diagnostiek en de originele waardes toe te voegen kan een oordeel worden gevormd over de waarde van ons anomaly experiment.

Deze code is gebruikt voor het rapport:

import os
import xlrd
import numpy as np
import pickle
import pandas as pd
XLDir = "/Volumes/Slot 4 2tB/Ziekenhuis data"
Ldir = os.listdir(XLDir)
XLList = [s for s in Ldir if s[-4:] == ".xls"]
XLCount = len(XLList)
SelCol = ['SeriesInstanceUID','ExamComments', 'ExamDiagnosis', 'ParameterMeasure', 'ParameterId','ResultIdentifier','DisplayUnit', 'ParameterName', 'DisplayValue']
SelColn = [61, 73, 74, 107, 108, 110, 113, 114, 124]

contrib = np.load('/Volumes/Slot 4 2tB/Ziekenhuis data/contrib.npy')
stop = 0

for xl in XLList:
    if stop >= 10:       # Maximaal 10 outlier onderzoeken
        break
    book = xlrd.open_workbook(XLDir + '/' + xl, logfile=open(os.devnull, 'w'))
    sh = book.sheet_by_index(0)
    if sh.ncols >= 129 and sh.cell(0,129).value =='FindingId' and sh.cell(1,61).value in contrib[:,0]: # Alleen waar deze cel FindingId bevat!"
        Data = []
        stop += 1
        for rx in range(sh.nrows):              # Inlezen spreadsheet in Data
            Data.append(sh.row_values(rx))

        Data = np.array(Data)                   # Omzetten naar numpy array
        Data = Data[:,SelColn]                  # Select only the 4 interesting columns
        Data = Data[Data[:,5] == 'Average']     # Select only 'Avarage' values
        DataPD = pd.DataFrame(Data, columns=SelCol)
        
        print
        print '**************************************  Anomaly nr.: ', stop, DataPD.loc[1,'SeriesInstanceUID'], ' ***************************************'
        print DataPD.loc[1,'ExamComments']
        print DataPD.loc[1,'ExamDiagnosis']
        p=contrib[contrib[:,0]==sh.cell(1,61).value]
        print 'Grootste bijdrage (%):'
        #print p[0,0].tolist()
        p = p[0,1].tolist()
        for i in range(10):
            print "{0:20} {1}".format(p[0][i], p[1][i])

        print DataPD.loc[:,['ParameterId', 'ParameterName', 'DisplayValue', 'DisplayUnit']]
        p=contrib[contrib[:,0]==sh.cell(1,61).value]

Diagnostiek - deel 3

Gisteren een mooi bestand kunnen maken zonder de userdefined variabelen en met de 'nan' waardes. Nu eens ff kijken of we de data inderdaad 'schoon ' kunnen krijgen. De sklearn preprocessing routines vereisen blijkbaar een array zonder string variabelen. Ik moet dus de 'onderzoeksidentifier' even verwijderen. Uiteraard zie ik daarbij eerst programmastukjes over het hoofd maar dan lijkt dat deel lekker te lopen.

Alleen sorteren blijft een worsteling. Om de 'outliers' te kunnen 'verklaren' heb ik de procentuele afwijking van het gemiddelde, per meetwaarde berekend. Uiteraard wil ik de grootste percentages met de title van de meetwaardes 'vooraan' hebben. Ik plak ze daarom in 1 array en probeer ze op percentage te sorteren. Hij blijft raar doen. Eerst sorteert hij steevast op de titels in plaats van op de namen. Daarna sorteert hij oplopend de 'normale percentages, dan volgen de exponentiële percentages en het eindigt met de nullen!? Alleen dat laatste lijkt een beetje op wat ik wil. Ik probeer allerlei sorteer variaties maar zonder veel succes. Pas als ik de programma volgorde omdraai, eerst sorteren en dan pas op elkaar plakken lijkt het resultaat beter. Gelukkig is dat mogelijk in Pyhon en Numpy doordat met de functie 'argsort' een array van de indexen wordt gemaakt van de gesorteerde lijst. Met die array kan ik dus zowel de waardes als de titels netjes sorteren. Eindelijk. Vanuit een matrix met 10128 onderzoeken met 280 parameters krijg ik 102 'anomalie meldingen' Dat lijkt behapbaar. Nu naar de volgende stap: rapportage. Ik ben benieuwd!    

>>> execfile('/Users/DWW/Documents/2 TTE_TEE outliers.py')
(10128, 280)
nu, gamma =  0.01 0.001 number outliers:  102  

Het programma zover:

#execfile('/Users/DWW/Documents/2 TTE_TEE outliers.py')
import os
import numpy as np

XH = np.load('/Volumes/Slot 4 2tB/Ziekenhuis data/X.npy')
collist = np.load('/Volumes/Slot 4 2tB/Ziekenhuis data/collist.npy')
R, X = XH[:,:1], XH[:,1:] # split in reference (R) and parameter values (X)

print X.shape

# Data schoning : Vul lege waardes (nan) met het gemiddelde van de colom + schaal de waarden tussen 0 en 1.
from sklearn.preprocessing import Imputer
imp = Imputer(missing_values='NaN', strategy='mean', axis=0)
imp.fit(X)
X = imp.transform(X)
from sklearn import preprocessing
min_max_scaler = preprocessing.MinMaxScaler()
X = min_max_scaler.fit_transform(X)

# En nu de daadwerkelijke 'anomaly detection'
xl, xb = X.shape
#parms = np.array(X, dtype=float)
av = np.average(X, axis = 0)
outliers = []
contrib = []
first = True
from sklearn import svm
# Testen verschillende waardes van SVM (support vector machine) parameters: nu and gamma for outlier detection.
for nu in [0.01 ]:
    for gamma in [0.001]:
        clf = svm.OneClassSVM(nu=nu, kernel="rbf", gamma=gamma)
        clf.fit(X)
        #print ('nu, gamma = ',nu,gamma)
        c=0
        for i in range(xl):
            if clf.predict(X[i]) == -1:
                c += 1
                outliers = outliers + [R[i]]                # list met SeriesInstanceUID
                h = np.absolute(np.subtract(av, X[i]))      # Calculate absolute difference between avarage and current value
                h = 100 * h / np.sum(h)                     # Calculate relative percentage
                s = np.argsort(h)                           # s = sortering index parameters
                co = collist[1:]                            # co = collist minus de onderzoeks identificatie
                h = np.vstack((co[s], h[s]))                # Combine difference with columnlist sorted
                h[:] = h[:,::-1]                            # Reverse list (large to small)
                if first:
                    contrib = [R[i] , h]
                    first = False
                else:
                    contrib = np.vstack((contrib, [R[i] , h]))
            #print h #h[:,-10:]
            #if c > 1:
            #    break
                    
        print 'nu, gamma = ',nu,gamma, 'number outliers: ',c # , i+1, X[i][0]
#print 'outliers = ', outliers
#print '# outliers = ', len(outliers)
#print 'contrib = ', contrib
np.save('/Volumes/Slot 4 2tB/Ziekenhuis data/outliers',outliers)

np.save('/Volumes/Slot 4 2tB/Ziekenhuis data/contrib',contrib)

dinsdag 3 november 2015

Diagnostiek - deel 2

Het valt in eerste instantie niet mee om de userdefined variabelen eruit te halen. Met het indrukwekkende commando


Data = Data[np.core.defchararray.rjust(Data[:,1],8) != 'USERDEFP']

lukt het uiteindelijk. (Het veld begint met USERDEFP gevolgd door een volgnummer)

Daarnaast wil ik, inplaats van de nullen in de nieuwe kolommen en rijen, gebruik maken van 'nan', de manier om in Python / Numpy aan te geven dat er geen data beschikbaar is . Dat maakt het berekenen van het gemiddelde, bij de data opschoning straks, meer betrouwbaar. Nulwaarden halen anders wellicht het gemiddelde omlaag.
Ook dit blijkt lastig vooral omdat het toevoegen van kolommen en rijen dezelfde array-structuur vereist. Dat had ik natuurlijk eerder ook al opgelost maar de korte methode voor het aanvullen met 'nan' (vermenigvuldigen met np.nan) blijkt iets te doen aan de datastructuur. Of heb ik toch ook iets anders aangepast? Hier wreekt zich het niet werken met versies want ik kan mijn eerdere code niet terugkijken. Nu ja, na veel experimenteren lijkt het weer gelukt. Weer een keer 11.000 excels door. Dat gaat weer even wat tijd nemen. Dus gelegenheid om dit verslagje te maken. :-)

Hier is de gebruikte code:

import os
import xlrd
import numpy as np
import pickle
import pandas as pd
XLDir = "/Volumes/Slot 4 2tB/Ziekenhuis data"
Ldir = os.listdir(XLDir)
XLList = [s for s in Ldir if s[-4:] == ".xls"]
XLCount = len(XLList)
SelCol = ['SeriesInstanceUID','ParameterId','ResultIdentifier','ResultValue']
SelColn = [61,108, 110, 111]

stop = 0
best = 0
collist = ['SeriesInstanceUID']
first = True # Eerste aanmaak van X (input matrix)
for xl in XLList:
    #stop += 1
    if stop > 10:
        break
    
    book = xlrd.open_workbook(XLDir + '/' + xl, logfile=open(os.devnull, 'w'))
    sh = book.sheet_by_index(0)
    if sh.ncols >= 129 and sh.cell(0,129).value =='FindingId': # Alleen waar deze cel FindingId bevat!"
        best +=1
        print best, xl
        Data = []
        for rx in range(sh.nrows):                                                      # Inlezen spreadsheet in Data
            Data.append(sh.row_values(rx))
        Data = np.array(Data)                                                           # Omzetten naar numpy array
        Data = Data[:,SelColn]                                                          # Select only the 4 interesting columns
        Data = Data[Data[:,2] == 'Average']                                             # Select only 'Avarage' values
        Data = Data[np.core.defchararray.rjust(Data[:,1],8) != 'USERDEFP']              # Remove USERDEFP columns
        R,C = Data.shape
        if R > 0:                                                                       # Anders zijn er geen 'Avarage' waardes aangetroffen
            if first:                                                                   # Eerste keer dat X wordt aangemaakt krijgt deze alleen 1 x de eerste onderzoeks identifier 'SeriesInstanceUID'
                X=np.array([[Data[0][0]]])
                first = False
            else:
                #print X.shape, len(X)
                X=np.vstack((X,np.append([Data[0][0]], np.nan*np.zeros(len(X[0])-1))))  # Als X al bestaat wordt er een record toegevoegd van de identifier en de huidige hoeveelheid kolommen (nan's)
    
            for i in range(len(Data)):                                                  # Alle rijen uit de 'uitgedunde excel' langs
                if Data[i][1] not in collist:                                           # 'ParameterID' niet in collist
                    collist = collist + [Data[i][1]]                                    # Voeg aan collist toe
                    X = np.append(X,np.nan*np.zeros((len(X),1)), 1)                     # Voeg kolom met nan's toe aan X
                X[(len(X)-1)][collist.index(Data[i][1])] = Data[i][3]                   # Fill proper column with Resultvalue

np.save('/Volumes/Slot 4 2tB/Ziekenhuis data/X',X)
np.save('/Volumes/Slot 4 2tB/Ziekenhuis data/collist',collist)

X = np.load('/Volumes/Slot 4 2tB/Ziekenhuis data/X.npy')
D_PD = pd.DataFrame(X)
print collist
print(D_PD.head(10))
print(D_PD.describe())