woensdag 26 mei 2021

03 CommonLit Readability Prize Kaggle

02 CommonLit Readability Prize Kaggle

 De eerste tests met een karakter gebaseerd NN zijn vrij dramatisch:


Poging 1 met dense NN



En poging 2

Self attention

Ik pas nu met name de learning rate aan naar 100 kleiner:


Dat ziet er iig al wat mooier uit. Tijd voor de eerste Kaggle submit! Het is een 'code competitie'. D.w.z. dat de volledige code via een ... moet worden aangeboden. Dat blijkt even lastig omdat niet alle libraries op Kaggle beschikbaar zijn. De selfattention kan ik wel toevoegen maar de cleantext module geeft wat problemen. Dan maar weer terug naar mijn eigen, eerder gemaakte opschoning subroutine.
Ik kom op uit op een score van 1.048 (topscore 26 mei 0.460). En daarmee op ongeveer de 1000e plaats. Hmmm. Maar goed. Het is het de eerste submit nog maar. Iig lijkt alles te werken. De voorspelde waarden liggen erg dicht bij elkaar:
[[-0.94634914]
 [-0.93451864]
 [-0.9363963 ]
 [-0.9408344 ]
 [-0.947149  ]
 [-0.94283646] 
[-0.947149 ]] 

Dit is iig wel dicht bij de gemiddelde waarde: -0.9593187699947071 






01 CommonLit Readability Prize Kaggle

Eindelijk weer een leuke Kaggle NLP competitie! De "CommonLit Readability Prize". Een wedstriijd om de complexiteit van teksten te waarderen. Uiteraard probeer ik dit niet op de klassieke wijze, met woord tokens maar wil ik meer info toevoegen door van de letters uit te gaan. De lengte van de woorden maar ook bijvoorbeeld de gebruikte tekens zullen gevoeldmatig mede de complexiteitswaarde bepalen.


Er zijn 2.834 teksten in de train-set (en maar 7 in de test-set) Dat is niet veel voor NLP. 

Eerst maar eens kijken hoe de teksten er uit zien. 

"All through dinner time, Mrs. Fayre was somewhat silent, her eyes resting on Dolly with a wistful, uncertain expression. She wanted to give the child the pleasure she craved, but she had hard work to bring herself to the point of overcoming her own objections.\nAt last, however, when the meal was nearly over, she smiled at her little daughter, and said, "All right, Dolly, you may go."\n"Oh, mother!" Dolly cried, overwhelmed with sudden delight. "Really?\nOh, I am so glad! Are you sure you\'re willing?"\n"I\'ve persuaded myself to be willing, against my will," returned Mrs. Fayre, whimsically. "I confess I just hate to have you go, but I can\'t bear to deprive you of the pleasure trip. And, as you say, it would also keep Dotty at home, and so, altogether, I think I shall have to give in."\n"Oh, you angel mother! You blessed lady! How good you are!" And Dolly flew around the table and gave her mother a hug that nearly suffocated her.'

Dit is en voorbeeld tekst. De bijbehoordende score is -0.3154 Wat wil dat nou zeggen?

Ok, eerst wat zijn de mogelijke scoringswaarden?

>>> np.max(y2)

1.7113898269999999

>>> np.min(y2)

-3.676267773

>>> np.average(y2)

-0.9593187699947071

>>> np.max(y2)-np.min(y2)

5.3876576

Laten we eens kijken hoe de eenvoudigste tekst er uit ziet

Deze tekst heeft de hoogste score (1.1711 ...)

'When you think of dinosaurs and where they lived, what do you picture? Do you see hot, steamy swamps, thick jungles, or sunny plains? Dinosaurs lived in those places, yes. But did you know that some dinosaurs lived in the cold and the darkness near the North and South Poles?\nThis surprised scientists, too. Paleontologists used to believe that dinosaurs lived only in the warmest parts of the world. They thought that dinosaurs could only have lived in places where turtles, crocodiles, and snakes live today. Later, these dinosaur scientists began finding bones in surprising places.\nOne of those surprising fossil beds is a place called Dinosaur Cove, Australia. One hundred million years ago, Australia was connected to Antarctica. Both continents were located near the South Pole. Today, paleontologists dig dinosaur fossils out of the ground. They think about what those ancient bones must mean.'

En deze de laagste (-3.6762 ...)

'The commutator is peculiar, consisting of only three segments of a copper ring, while in the simplest of other continuous current generators several times that number exist, and frequently 120! segments are to be found. These three segments are made so as to be removable in a moment for cleaning or replacement. They are mounted upon a metal support, and are surrounded on all sides by a free air space, and cannot, therefore, lose their insulated condition. This feature of air insulation is peculiar to this system, and is very important as a factor in the durability of the commutator. Besides this, the commutator is sustained by supports carried in flanges upon the shaft, which flanges, as an additional safeguard, are coated all over with hard rubber, one of the finest known insulators. It may be stated, without fear of contradiction, that no other commutator made is so thoroughly insulated and protected. The three commutator segments virtually constitute a single copper ring, mounted in free air, and cut into three equal pieces by slots across its face.'

Het lijkt er dus op dat de score hoger wordt naarmate de tekst minder complex wordt. Dit is de verdeling van de waarden:


Lijkt een aardige normaalverdeling maar wil wel zeggen dat de complexe teksten (55) en de meest eenvoudige (31) redelijk ondervertegenwoordigd zijn. ([ 55., 120., 280., 402., 490., 563., 458., 312., 123.,  31.]) Misschien zijn 5 bins beter:


 

Er is ook nog andere info beschikbaar zoals een url een licence en een standard error. Misschien later wat mee doen. 

Index(['id', 'url_legal', 'license', 'excerpt', 'target', 'standard_error'])

De teksten zijn gemiddels 972 karakters bestaande uit gemiddeld 172 woorden lang. (min 125 / max 205 woorden)


       



dinsdag 23 maart 2021

NLP Autoencoders testjes

Autoencoders zijn NN's die de eigen input proberen te reproduceren maar dan meestal via een smalle laag in het NN zodat er een soort 'compressie' plaatsvindt. Ik wil eens kijken of dat een rol kan spelen in NLP (natural language processing) met letters. Ik voer een heleboel woorden in een kleine, eenlaags NN en kijk of deze weer gereproduceerd kunnen worden. De maximale woordlengte is 15 karakters.

Met 1 laag van 50 'neuronen' haal ik een validation loss van 0.1838 na ongeveer 8 epochs. Het is grappig om te zien hoe goed die dan woorden kan terugfilmen:

Do              Do

NOT             MNS

count           boumt

on              on

the             shd

rating          r`thne

and             `oc

 Let op dat bijvoorbeeld 'NOT' en 'MNS'  wel verschillen maar dat alle letters dicht bij elkaar in het alfabet (de ascii code) liggen.

Met een dubbele laag van 2x50 komt het NN maar tot 0.1839. Niet veel verschil maar toch een duidelijke richting lijkt het.


Als ik een loss functie definieer met mse (mean squared error - hij stond op 'binary_crossentropy' kan ook niet goed zijn achteraf gezien) duikt de validation loss naar beneden van .1838 naar 0.0079. Als ik vervolgens bij de printout van de woorden niet met 'int' afrond maar met 'round' krijg ik een uiterst betrouwbaar resultaat. Maar een enkel foutje hier en daar:

turns           turns

out,            out,

unfortunately   unfortunateky

yes.            yes.

Jackson         Jackson

needs           needs

to              to

Lisa            Lisa

to              to

help            help

him             him

assassinate     assassinate

the             the

Director        Director

of              of

Homeland        Homeland

Security        Security

by              by

moving          moving

Het blijkt dus in ieder geval mogelijk om in 1 layer met 100 neuronen 611.526 verschillende woorden!!! met een behoorlijke betrouwbaarheid vast te leggen. Natuurlijk moet hierbij wel worden bedacht dat de validatieset voor een belangrijk deel dezelfde woorden zal bevatten als de trainset.

Nu eens kijken wat het resultaat is bij een kleinere layer van 50.  

Ofschoon de validation loss gelijk is (0.0079) is het resultaat beduidend slechter:

turns           tvrns

out,            out1

unfortunately   vnfprtun`tcl{

yes.            zds3

Jackson         Kackson

needs           neecs

to              to

Lisa            Mis`

to              to

help            help

him             him

Vreemd. Niet dat het resultaat slechter is maar wel dat de validation loss gelijk blijft. Snap ik even niet. 


Ik schrijf een functietje om gewoon wat woorden te testen. Het NN blijkt ook heel goed Nederlands te kunnen hoewel ik denk dat er alleen Engelstalige woorden in de trainset zitten. Hier wat voorbeelden:

>>> test("apple")

apple

>>> test("olifant")

olifant

>>> test("banaan")

banaan

>>> test("citruspers")

citrvspers

>>> test("equivalent")

equivalent

>>> test("superdouper"

... )

superdouper

>>> test("een en twee")

den#dn#twee 

Grappig, niet waar?

Maar ook :

>>> test("100")

100

>>> test("101")

101

>>> test("104")

104

>>> test("10194")

10183

>>> test("4579474874874c")

8479573764763c☺

>>> test("4579474874874C")

7479573775763C☺

>>> test("abcdefghi")

abcddfghi

>>> test("abCdefghi")

abCddfghi

woensdag 3 februari 2021

Neural Circuit Policies 01

Een flinke uitdaging bij neurale netwerken is het toenemende formaat en de hoeveelheid data die nodig lijkt om steeds betere resultaten te krijgen. Zo bestaat gpt-3 al uit 175 miljard parameters. Net iets te groot om op een huiscomputer te proberen :-)

Via SingularityHub kom ik op een nieuwe benadering van neurale netwerken, losjes gebaseerd op het neurale systeem van  C. elegans, een klein wormpje. (onderzoek van MIT e Austria’s Institute of Science and Technology). In dit onderzoek lijkt met een veel kleinere configuratie heel goede resultaten te worden bereikt. Met name hebben ze de mogelijkhden voor zelfrijdende systemen uitgetest. 

Hier is een eenvoudig voorbeeld van zo'n netwerkstructuur:       

In dit geval dus input van 2 sensoren, 3 'inter neuronen', 4 'commondo neuronen' en 1 'motor neuron'. Het lijkt erop dat de 'inter neuronen' gezien kunnen worden als de 1e 'hidden layer' en de 'command neuronen' als de laatste hidden layer. Voor de rest zijn er maar een beperkt aantal, maar specifiek georganiseerde verbindingen. Dit is de definitie 

ncp_arch = wirings.NCP(
    inter_neurons=3,    # Number of inter neurons
    command_neurons=4,  # Number of command neurons
    motor_neurons=1,    # Number of motor neurons
    sensory_fanout=2,   # How many outgoing synapses has each sensory neuron
    inter_fanout=2,     # How many outgoing synapses has each inter neuron
    recurrent_command_synapses=3,  # Now many recurrent synapses are in the
                        # command neuron layer
    motor_fanin=4,      # How many incomming synapses has each motor neuron
)
Eens kijken wat er gebeurd als we het model wat vergroten.


Sensor2 wordt vergelijkbaar verbonden als sensor0. En dat wordt ook gedaan als we nog meer sensoren toevoegen. In de rest van het model  worden hier en daar wat extra verbindingen gelegd. O.a.tussen neuron3 en neuron1. Met ook een extra interneuron krijgen we deze configuratie:

Interessant. Ik verwachtte een regelmatiger verdeling van de verbindingen tussen de sensors en de interneurons maar sensor2 wordt nu gelijk aan sensor1 verbonden. (Wat de rood en groene verbindingen betekenen weet ik nog niet. Het is een speciale weergave functie die voor deze nn's is ontwikkeld)

Voor een 1e spoorzoekende robot is deze NCP gebruikt:
Dus het kan wel complexer als je wil :-) Toch zijn er veel minder verbindingen (en neuronen) als in een gangbaar 'fully connected netwerk.' 
Hier vind ik de verklaring van de groene en rode verbindingen:
synapse_colors = {"excitatory": "tab:green", "inhibitory": "tab:red"}
Nu nog uitzoeken wat dat inhoud.





maandag 18 januari 2021

Toch weer even met mnist spelen

 Al heel lan g niets meer met de AI dataset der datasets gedaan: De Mnist geschreven nummerset met afbeeldingen van 0 t/m 9 in een 28x28 formaat.

Zoals deze:

  

De reden hiervoor is een artikeltje over het leren zoals mensen dat doen op SingularityHub. 

Er wordt vaak aangegeven dat de huidige NN's zo ontzettend veel voorbeelden moeten hebben om een beetje kennis op te doen. Dit in schijnbare tegenstelling tot mensen die aantal voorbeelden al genoeg lijken te hebben. Vaak wordt echter denk ik vergeten dat mensen als baby en kind (en volwassene) eerst een heleboel voorbeelden waarnemen voordat ze hiertoe in staat zijn.  Dagelijks neemt men elke seconde nieuwe beelden op die als ervaring gebruikt kunnen worden. 

Babies beginnen, meen ik met vage, en dus vereenvoudigede, beelden omdat ze simpleweg nog niet de goede lenzen hebben. Maar ze leggen wel al langzamerhand verbanden, Geleidelijk wordt het zicht beter en daarmee de complexiteit van de afbeeldingen groter. Het leek mij wel leuk om dat ook eens met een NN te proberen en welke is dan beter dan mnist!

Eerst draai ik een oud, ongewijzigd model om een 'baseline' te krijgen:

Een test accuracy van 0.9947 %. Aardig om mee te beginnen. 
Een blurfilter vind ik in cv2 met de toepasselijke naam 'cv2.blur'. Daat kan ik ook een blurfactor invullen als tuple (bijvoorbeeld (5,5)). Ik neem even aan dat dat vertikale en horizontale blur betreft.
Hier een voorbeeld:

Blurred (5, 5)

Blurred (7, 7)

Ik kan mij een beetje voorstellen dat een baby een dergelijk beeld 'van de wereld' zou kunnen hebben.
Eerst maar eens trainen op blurred(5, 5). Ook op de y_train waarden gebruik ik de blur functie waardoor bijvoorbeeld  
        y_train[0] = array([0., 0., 1., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)
Deze waarden krijgt: 
        y_train_blur[0] = array([0.08, 0.04, 0.04, 0.12, 0.12, 0.16, 0.16, 0.16, 0.08, 0.16], dtype=float32)

Ik weet nog niet waar ik tijdens de 'blur-training' de waarde op uit wil laten komen. (Babies zullen ook niet direct weten wat ze met een cijfer 3 aanmoeten ;-) Dus dit lijkt wel even een aardige manier om mee te beginnen.


Oke, niet een verbetering maar ook niet slecht met een testaccuracy van 0.9942. Bij pretraining met dubbel blurring (7,7) gaan we terug naar 0.9930. 

Ik probeer eens om zowel een (7,7) als een (5,5) blurring achter elkaar als pretraining uit te voeren. Ik kom dan terug op onze benchmark 0.9947. 

Het helpt blijkbaar een beetje om y_train ook te blurren want zonder dat zaken we naar 0.9942. 

Bij 'halfsize double blurring' (5,5) en (3,3) ga ik voor het eerst iets (1/100%) omhoog t.o.v. de benchmark: 0.9948 

Ik probeer of het toevoegen van wat noise kan helpen. We duiken weer omlaag naar 0.9935

Gezien het 'succes' van de 'halfsize blurring' probeer ik ook eens of ik blurring in meerdere stappen kan laten trainen. [(9,9), (7,7), (5,5), (3,3)] Helaas is dat geen goed idee : 0.9925

Goed, terug naar 'halfsize double blurring' en eens kijken of ik hier iets met de y_train waarden kan doen. Daar veel noise toevoegen is geen goed idee (0.9904)

Dat gaat al veel beter bij een kleine noisefactor van 0.01. Ik ben weer terug bij 0.9948

Misschien is het beter om te starten met waarden rond de 0,5 ipv 0,0? Nee: 0.9939

  

Zou ik ze gewoon vast naar 0 moeten laten trainen? --> Nee, helaas (0,9944)

Misschien pretrainen met niet geblurde afbeeldingen? (Dat haalt wel mijn feitelijke uitgangspunt met de slecht ziende babies een beetje onderuit) Wow, niet verwacht: 0,9914! Dat is wel heel erg omlaag. Het lijkt erop dat pre-trainen op bovengenoemde manieren nog niet veel oplevert.

 ðŸ˜’







maandag 4 januari 2021

Bert , terug van weggeweest

Om verder te kijken wat we in de (IT) bedrijfsprocessen met AI zouden kunnen doen heb ik opnieuw een setje incidenten (GCS - Global Customer Service) geladen (24.586 records) maar deze keer met de tijdsduur die het heeft genomen om het incident te behandelen. Hiermee wil ik kijken of ik kan voorspellen hoe moeilijk (tijdsintensief) het betreffende incident is. (Of dus 'gaat zijn').

Omdat de tijdsduur in seconden staat opgenomen lijkt het mij zinvol om een logaritmische schaal toe te passen. Hierdoor krijgen we uitkomsten van tussen de 0 en de 7 (6.792 om precies te zijn). 

Bij het omzetten naar logaritmische waarden krijg ik -inf waarden als de tijdsduur 0 is. Dat moet ik eerst even corrigeren om het model goed te laten werken.

Hier is een verdeling van de (naar beneden) afgeronde logaritmische waarden:

Als input gebruik ik de 'Short description', de 'Service' en de 'Assignmentgroup' en de 'Description' tot een maximum lengte van 128 tokens. De Service blijkt uitsluitend 'GCS Helpdesk' te zijn, dus die kan ik eigelijk uitsluiten. De assignmentgroup moet er ook eigelijk uit. Dat is wellicht juist iets wat later op basis van de complexiteit moet worden gestuurd.   

Normaal heb ik met het Bert model classificaties uitgevoerd. Nu gaat het om een regressie probleem: Het vinden van een getal welke de 'duur' (complexiteit) aangeeft. De loss pas ik aan naar 'mse' (mean squared error)  Al snel weet ik het model nu aan de praat te krijgen. 


Bij 4 epochs vindt die het beste resultaat. Maar hoe goed doet die het nou? Een error van, zeg maar, 0.82 is dat nu goed (genoeg) of niet?
Door ook weer hier te tellen hoeveel voorspellingen goed zouden zijn krijg je een beetje een indruk.  Dit wordt in dit geval bepaald door de 'acceptabele afwijking'. 
Bij een maximale afwijking van 1.0 krijg ik   0,79% correctheid. Dus in vrijwel 80% van de gevallen lijkt de complexiteit redelijk juist te voorspellen. 

Of dit ook in de praktijk bruikbaar kan gaan zijn moet nog blijken. 

Ik ga nu ook even kijken of ik de hierboven genoemde overbodige 'features' eruit kan halen en alleen op basis van de korte beschrijving andere resultaten krijg. 
  

 

Dat lijkt toch wat minder positief. 

 print("Percentage correct :",count / len(test_y))

Percentage correct : 0.4493877551020408


Als ik de lange omschrijving toch mee neem krijg ik dit resultaat:

Percentage correct : 0.6557142857142857


Met het toevoegen van alle initiele (door de gebruiker ingevulde) invoervelden, zoals 'Incident Type', ('Service'), 'Configuration item', 'Impact', 'Short description' en 'Description' haal ik ruim 70%

Percentage correct : 0.7157788944723618



Dat valt niet tegen.

Incident type = {'Question', 'Complaint', 'Incident'}
Configuration item verwijst meestal naar de betreffende applicatie ('Incenter Online' of bijv. 'MS Office') maar kan ook nar en locatie verwijzen (bijv. 'ServiceMax NAM')
Impact = {'3 - Low', '2 - Medium', '1 - High'}

Als ik een opschoning doe door veel voorkomende afkortingen om te zetten alsmede email adressen weg te halen wordt het nog beter (hoewel de beste 'val loss' iets hoger lijkt uit te komen- ruim 74%:

Percentage correct : 0.744321608040201
Een 2e keer hetzelfde, met anders geshuffelde gegevens, geeft iets vergelijkbaars.
 
Percentage correct : 0.7276381909547739


>>> confusion_matrix(np.round(test_y),np.round(predicts))
array([[   0,    0,    4,    4,   19,    1,    0,    0],
       [   0,    0,    1,    0,    0,    0,    0,    0],
       [   0,    0,  404,  595,  205,    6,    0,    0],
       [   0,    0,  144,  764,  738,   10,    0,    0],
       [   0,    0,   16,  181, 1017,   45,    0,    0],
       [   0,    0,    5,   67,  423,   51,    2,    0],
       [   0,    0,    7,   42,  186,   29,    3,    0],
       [   0,    0,    0,    1,    4,    1,    0,    0]], dtype=int64)