Na het avontuur met letter - leren, waar ik vast nog eens op terug kom, nu even weer iets anders. Ik heb al eens eerder geexperimenteerd met documentvergelijking. Dat lijkt mij een zinvolle toepassing, Ook bijvoorbeeld in het checken in een 'incident registratie systeem' of een bepaald probleem al eens eerder is gemeld. Ik zoek of er al eens iets met Bert is gedaan (uiteraard) en vind deze site.
Het installeren met pip van "semantic-text-similarity" gaat goed. Evenals de import. Maar het 'activeren' van het model geeft, na het initieel juist downloaden dan de model-date, een error. Op een of andere manier zijn er onvoldoende rechten op de plek waar het model 'ge-unzipped' moet worden. Met wat aanpassingen in de code weet ik dat te omzeilen. Hierdoor krijg ik gelijk de mogelijkheid, denk ik, om andere Bert modellen uit te proberen. Maar eerst maar eens deze.
Ik gebruik een selectie van incident teksten uit ServiceNow. Ik wil nu ook de uitgebreidere omschrijvingen meenemen maar zie tot mijn verbazing dat het Snow rapport ze wel selecteerd maar dat ze in de download niet worden meegenomen in de huidige Snow versie. Eerder was dat geen probleem. Ik kijk naar de verschillende 'download-opties' (xlxs / csv / pdf / json) en zie dat alleen json ze wel meeneemt. Het rare is dat daar helemaal niet naar de kolomselecties wordt gekeken en ik krijg dus nu alle mogelijke velden mee. Maar goed, dat is beter dan te weinig.
Het lukt mij vrij snel om en programma te maken dat de teksten uit de verschillende incidenten met elkaar vergelijkt en er een 'similarity score' van 0 tot 5 aan geeft.
Hier een paar voorbeelden:
89 ***************************************************************************************************** 4
Not able to login into skype, error could not login not able to login into skype, error could not login
935 ====================================================================================================
Skype is not able to login skype is not able to login
85 **************************************************************************************************** 3
WO-01368223 no PA available kindly,i couldnot find a pa for the work order wo-01368223 ,please,fyka
3195 ====================================================================================================
kindly your support is highly appreciated as I didn’t found the PA for the work order WO-01396592 after making Many Synchronization.
73 **************************************************************************************************** 2
TS: Time Statement missing/wrong since 9th of february, timestatement is missing entries
348 ====================================================================================================
TS: do not receive Time Statements even with today's time accounting my reported times were not counted. my time-writing is since the beginning of february.
Valt mij niet tegen. Het gaat natuurlijk hier om kortere teksten maar er lijkt aardig wat 'logica' gebruikt te worden.
Nadeel is wel dat het (relatief) enorm lang duurt om teksten met elkaar te vergelijken. Hij heeft er ongeveer 10 uur voor nodig gehad om 153 documenten met 3598 te vergelijken. Even rekenen ... zijn dus 917 vergelijkingen per minuut. hmmmm 15 per seconde. Is op zich niet slecht maar eigelijk wil je misschien ongeveer 3598 * 3598 documenten vergelijken. Dan duurt het 235 uur. Tja...
Eens kijken of het ook nog sneller kan.
dinsdag 24 maart 2020
zondag 22 maart 2020
Leren met letters 08 (83%) - Een eigen Keras layer
Vandaag heb ik mij verdiept in het maken van een echte eigen Keras layer. door de gekozen vaste structuur van mijn data heeft het model met chars2vec veel ruimte nodig om de data op te slaan. De v50 versie gaat prima, de v100 ook nog wel maar de v200 lijkt wel de limiet te zijn. Ik zou zo graag ook nog eens de V300 versie testen. Bert werkt tenslotte met 768 vectoren dus waarom ik niet met minstens 300 😀 ?
De oplossing zou mogelijk zijn om de chars2vec conversie als eerste layer in het model op te nemen. Dan bied ik gewoon de teksten aan als list en de eerste layer maakt er de mooie vector van gebaseerd op chars2vec.
Ik probeer deze eenvoudige 'class' toe te voegen:
class C2v(Layer):
def __init__(self, units=batchsize, input=[]):
super(C2v, self).__init__()
def call(self, inputs):
return conv_texts_c2v(inputs)
De conv_texts_c2v splitst de input op in losse teksten en laat ze in conv_text_ch2v per tekt eerst wat opschonen rond tags en punctuaties en roept vervolgens het c2v_model.vectorize_words aan om alles om te zetten naar passende vectoren.
Na wat experimenteren lijkt de syntax in ieder geval goed. Helaas komt er een foutmelding dat dit zo niet is toegestaan:
tensorflow.python.framework.errors_impl.OperatorNotAllowedInGraphError: iterating over `tf.Tensor` is not allowed in Graph execution. Use Eager execution or decorate this function with @tf.function.
Ik probeer uiteraard de 'decorating optie' maar dat geeft helaas nog geen verbeteringen. Ook 'eager excecution', waar eerder naar verwezen wordt lijkt hier helaas niets te doen.
Het is mij nog niet duidelijk wat er nu precies fout gaat maar het lijkt te maken te hebben met de tensorflow graph-methode die bepaalde zaken niet toestaat.
Bij de keras embedding layer is het toch ook mogelijk om bijvoorbeeld pretrained word2vec te gebruiken. De code daarvan geeft mij helaas niet veel houvast.
De oplossing zou mogelijk zijn om de chars2vec conversie als eerste layer in het model op te nemen. Dan bied ik gewoon de teksten aan als list en de eerste layer maakt er de mooie vector van gebaseerd op chars2vec.
Ik probeer deze eenvoudige 'class' toe te voegen:
class C2v(Layer):
def __init__(self, units=batchsize, input=[]):
super(C2v, self).__init__()
def call(self, inputs):
return conv_texts_c2v(inputs)
De conv_texts_c2v splitst de input op in losse teksten en laat ze in conv_text_ch2v per tekt eerst wat opschonen rond tags en punctuaties en roept vervolgens het c2v_model.vectorize_words aan om alles om te zetten naar passende vectoren.
Na wat experimenteren lijkt de syntax in ieder geval goed. Helaas komt er een foutmelding dat dit zo niet is toegestaan:
tensorflow.python.framework.errors_impl.OperatorNotAllowedInGraphError: iterating over `tf.Tensor` is not allowed in Graph execution. Use Eager execution or decorate this function with @tf.function.
Ik probeer uiteraard de 'decorating optie' maar dat geeft helaas nog geen verbeteringen. Ook 'eager excecution', waar eerder naar verwezen wordt lijkt hier helaas niets te doen.
Het is mij nog niet duidelijk wat er nu precies fout gaat maar het lijkt te maken te hebben met de tensorflow graph-methode die bepaalde zaken niet toestaat.
Bij de keras embedding layer is het toch ook mogelijk om bijvoorbeeld pretrained word2vec te gebruiken. De code daarvan geeft mij helaas niet veel houvast.
zaterdag 21 maart 2020
Leren met letters 07 (83%) - Door naar vectorsize 100
Na het laatste succes door enerzijds de stride in de conv layer terug naar 2 te brengen en anderzijds ipv een maxpooling een avarage pooling layer te nemen (ik heb ook nog de sigmoid activatie bij de selfattention layer door een relu vervangen maar dat leverde alleen iets tijdswinst), probeer ik nu weer even of de 100 vector ons naar 83% kan brengen.
En inderdaad we komen voorbij de 83! Met ook een F1 van ruim 83%.
Wellicht is dit model nog door te trainen.
En inderdaad we komen voorbij de 83! Met ook een F1 van ruim 83%.
Wellicht is dit model nog door te trainen.
vrijdag 20 maart 2020
Leren met letters 06 (82%) - Aangepaste stride conv-layer
Soms kunnen wat kleine aanpassingen weer voor verbetering zorgen. Ik heb de stride van de conv layer van 5 naar 3 teruggebracht. (conv naar 7 maakte het veel slechter) Hoewel ik, voor de snelheid van het proberen van verschillende varianten weer teruggegaan ben naar de 50 vector kom
ik in de nieuwe test uit op een kleine 82%! Grappig is wel dat nu ook de train accuracy lager blijft uitkomen. Mogelijk duidt dat op minder overfitting. (Meer generalisatie)
ik in de nieuwe test uit op een kleine 82%! Grappig is wel dat nu ook de train accuracy lager blijft uitkomen. Mogelijk duidt dat op minder overfitting. (Meer generalisatie)
En met een stride van 2 nog beter. Nu ook boven de 82:
F1 = [0.817] |
donderdag 19 maart 2020
Leren met letters 05 (81%) - Grotere char-vector sizes
Dit is het resultaat van 'doorleren' met een dropout van 30%
Zoals aangegeven volgt de validation accuracy nu mooi de training accuracy, zoals te verwachten op een iets lager niveau. Helaas breekt die af bij de 71 epochs door de early stopping van 10 x geen val_loss verbetering. Dus erg veel hoger komt die helaas niet.
Ik besluit de nacht te gebruiken om 100 in plaats van 50 features voor chars2vec te testen. De dataset is nu opnieuw te groot (8.94 GB) voor mijn pc-geheugen en het programma blijkt te zijn afgebroken. Gelukkig gaat de conversie (gek genoeg) pas fout bij het proberen op te slaan van de data. Door wat overbodige variabelen weg te gooien lukt het toch om de save uit te voeren en het programma verder te laten trainen.
We komen nu boven de 81% val acc met een F1 van 81%. Niet slecht! (Maar nog niet bij de gewenste 85% 😒). Wellicht dat wat dropout nu wel kan gaan helpen?
Helaas, de dropout trekt alleen de train acc omlaag maar neemt de test/val acc niet mee omhoog. Wel grappig overigens dat de val acc voornamelijk boven de train acc blijft.
Ook een test met dubbele layers, met vectorvergroting naar 200 (max_sent = 10, max_words = 20, max_chars = 15) en een correctie omdat er nog een deling door max uitgevoerd werd, toch geen hogere vall_acc score helaas.
Zoals aangegeven volgt de validation accuracy nu mooi de training accuracy, zoals te verwachten op een iets lager niveau. Helaas breekt die af bij de 71 epochs door de early stopping van 10 x geen val_loss verbetering. Dus erg veel hoger komt die helaas niet.
Ik besluit de nacht te gebruiken om 100 in plaats van 50 features voor chars2vec te testen. De dataset is nu opnieuw te groot (8.94 GB) voor mijn pc-geheugen en het programma blijkt te zijn afgebroken. Gelukkig gaat de conversie (gek genoeg) pas fout bij het proberen op te slaan van de data. Door wat overbodige variabelen weg te gooien lukt het toch om de save uit te voeren en het programma verder te laten trainen.
We komen nu boven de 81% val acc met een F1 van 81%. Niet slecht! (Maar nog niet bij de gewenste 85% 😒). Wellicht dat wat dropout nu wel kan gaan helpen?
Helaas, de dropout trekt alleen de train acc omlaag maar neemt de test/val acc niet mee omhoog. Wel grappig overigens dat de val acc voornamelijk boven de train acc blijft.
Ook een test met dubbele layers, met vectorvergroting naar 200 (max_sent = 10, max_words = 20, max_chars = 15) en een correctie omdat er nog een deling door max uitgevoerd werd, toch geen hogere vall_acc score helaas.
woensdag 18 maart 2020
Leren met letters 04 (79%) - Tests dropout/double layers/normalisaties
Oke, na het succes van de chars2vec conversie nu eens kijken of we de resultaten nog verder kunnen verbeteren. Huidige score is een test accuracy van bijna 80%. Met een F1 van ook bijna 80: (0.7999).
De accuracy loopt nog omhoog na 60 epochs maar de test acc lijkt wat af te vlakken. Doortrainen heeft dus vermoedelijk maar beperkt zin.
Ik test wat zaken achter elkaar:
De accuracy loopt nog omhoog na 60 epochs maar de test acc lijkt wat af te vlakken. Doortrainen heeft dus vermoedelijk maar beperkt zin.
Ik test wat zaken achter elkaar:
- dropout layers om overfitting te voorkomen. Eerst maar eens 20% bij elke layer.
- dropout 50%
- dropout 10%
- dubbele layerset. Ik zie echter te laat dat ik ook de normalisatie layer actief heb gemaakt en de 'input layer' heb gekopieerd.
- dubbele layerset met correcties (normalisatie + inputlayer weg)
- verschillende locaties van de normalisatie layer. (na dense, na conv)
- normalisatie tussen conv en aparte activation layer
Mislukken, zoveel leuker dan nooit proberen :-) |
Dit is een voorbeeld van het 'dropout 10%' resultaat:
Door de early stopping op '10' te zetten kan ik een dropout van 30% doortrainen naar 60 epochs met dit resultaat:
Het aardige is dat, hoewel de accuracy minder hard klimt dan oorsponkelijk, de validation accuracy nu 'meegroeid'. Tussen de 40 en 60 komt er ongeveer 3% bij. Dat zou kunnen betekenen dat we met 100 epochs in de buurt van de gezochte 85% validation accuracy kunnen komen. (2x3% minus de afvlakking). Proberen maar!
Ondertussen eens kijken of ik een keras layer van de words2vec zou kunnen maken zodat ik de data niet in zijn geheel van tevoren hoef te converteren en dus ook mogelijk de '300 feature-variant' in mijn computers geheugen past.
Het aardige is dat, hoewel de accuracy minder hard klimt dan oorsponkelijk, de validation accuracy nu 'meegroeid'. Tussen de 40 en 60 komt er ongeveer 3% bij. Dat zou kunnen betekenen dat we met 100 epochs in de buurt van de gezochte 85% validation accuracy kunnen komen. (2x3% minus de afvlakking). Proberen maar!
Ondertussen eens kijken of ik een keras layer van de words2vec zou kunnen maken zodat ik de data niet in zijn geheel van tevoren hoef te converteren en dus ook mogelijk de '300 feature-variant' in mijn computers geheugen past.
Leren met letters 03 (80%) - Introductie chars2vec module
Met deze exercitie hoop ik de ingebouwde (semantische?) betekenis van de woorden te behouden door uit te gaan van codering van letters in plaats van de gangbare codering van woorden, Dat is enigzins succesvol maar haalt het nog bij lange na niet bij de resultaten die met woordcoding worden gehaald. Een belangrijke missende factor is de embedding ofwel word2vec methodiek. Hierbij wordt dus de betekenis van woorden in hun onderlinge relatie in de zinnen geleerd.
Ik had stiekum gehoopt dat dit, door de manier van aanbieden van de data, 'meegeleerd' zou worden. Omdat ik nog niet precies snap hoe het algoritme zelf in detail in elkaar steekt is het versterken van het model met een eigengebouwde 'char2vec' oplossing nog geen optie. Gelukkig vind ik anderen die zich met dezelfde problematiek hebben bezig gehouden.
Met een eenvoudige import van de python 'chars2vec' module (en wat gepruts aan het programma) kan ik de eerder gebruikte documenten omzetten naar voorgetrainde vectoren. Er is keuze uit verschillende voorgetrainde formaten dus uiteraard 😁kies ik eerst de grootste (300). Dat leidt tot een memory error dus ik besluit de nacht te gebruiken voor een hertraining met het (kleinste) 50 vectoren formaat.
En succes!
We schieten omhoog van 66% test accuracy naar ruim 80%. Wow!!
Met een F1 van ook bijna 80: (0.7999).
Dat is nog eens vooruitgang! Nog niet de streefwaarde van 85 testacc of meer maar we komen in ieder geval behoorlijk in de buurt.
Ik had stiekum gehoopt dat dit, door de manier van aanbieden van de data, 'meegeleerd' zou worden. Omdat ik nog niet precies snap hoe het algoritme zelf in detail in elkaar steekt is het versterken van het model met een eigengebouwde 'char2vec' oplossing nog geen optie. Gelukkig vind ik anderen die zich met dezelfde problematiek hebben bezig gehouden.
Met een eenvoudige import van de python 'chars2vec' module (en wat gepruts aan het programma) kan ik de eerder gebruikte documenten omzetten naar voorgetrainde vectoren. Er is keuze uit verschillende voorgetrainde formaten dus uiteraard 😁kies ik eerst de grootste (300). Dat leidt tot een memory error dus ik besluit de nacht te gebruiken voor een hertraining met het (kleinste) 50 vectoren formaat.
En succes!
We schieten omhoog van 66% test accuracy naar ruim 80%. Wow!!
Met een F1 van ook bijna 80: (0.7999).
Dat is nog eens vooruitgang! Nog niet de streefwaarde van 85 testacc of meer maar we komen in ieder geval behoorlijk in de buurt.
maandag 16 maart 2020
Leren met letters 02 (66%) - Normbepaling (85%) - data cleansing - layer volgorde
Op deze site wordt dezelfde sentiment analyse uitgevoerd met de movie database en dan met het gebruik van woorden als tokens. De resultaten daar lijken een aardige 'streefnorm' te geven.
Met dit eenvoudige embed / lstm model komt men daar tot een test accuracy van 85%
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_3 (Embedding) (None, 100, 100) 9254700
_________________________________________________________________
lstm_1 (LSTM) (None, 128) 117248
_________________________________________________________________
dense_3 (Dense) (None, 1) 129
=================================================================
Total params: 9,372,077
Trainable params: 117,377
Non-trainable params: 9,254,700
Het laatste testmodel komt ongeveer tot 64% dus nog een stevige weg te gaan. Eerst even kijken of ik iets mis in de data-voorbehandeling. Tags, punctuaties, enkelvoudige karakters en dubbele spaties worden verwijderd. Ik neem de "tag-verwijderfunctie" over. Die geeft inderdaad mogelijk onnodige onzin. De andere laat ik voorlopg in mijn model intact. Punctuatie heeft m.i. betekenis. Ik kan hier wellicht later nogeens op terugkomen.
Dit is het resultaat zonder de tags:
Die lijkt wel heel erg op de laatste met tags. Dat heeft dus niet veel uitgehaald. Wel is de F1 van 63 naar bijna 64% gegaan. Maar dat kan een random effect zijn.
Ik test of de volgorde van de nu gebruikte layers veel uitmaakt.
Met dit eenvoudige embed / lstm model komt men daar tot een test accuracy van 85%
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_3 (Embedding) (None, 100, 100) 9254700
_________________________________________________________________
lstm_1 (LSTM) (None, 128) 117248
_________________________________________________________________
dense_3 (Dense) (None, 1) 129
=================================================================
Total params: 9,372,077
Trainable params: 117,377
Non-trainable params: 9,254,700
Het laatste testmodel komt ongeveer tot 64% dus nog een stevige weg te gaan. Eerst even kijken of ik iets mis in de data-voorbehandeling. Tags, punctuaties, enkelvoudige karakters en dubbele spaties worden verwijderd. Ik neem de "tag-verwijderfunctie" over. Die geeft inderdaad mogelijk onnodige onzin. De andere laat ik voorlopg in mijn model intact. Punctuatie heeft m.i. betekenis. Ik kan hier wellicht later nogeens op terugkomen.
Dit is het resultaat zonder de tags:
Die lijkt wel heel erg op de laatste met tags. Dat heeft dus niet veel uitgehaald. Wel is de F1 van 63 naar bijna 64% gegaan. Maar dat kan een random effect zijn.
Ik test of de volgorde van de nu gebruikte layers veel uitmaakt.
- uitgangspunt: dense/conv/maxp/atten (+flatten + dens)
- dense/atten/conv/maxp
- dense/conv/atten/maxp
Maar de oorspronkelijke volgorde lijkt tot nu toe toch de beste resultaten te geven.
De bedoeling is om een serie layers te gaan herhalen analoog aan de opbouw van Bert. Ik test een eerst een verdubbeling'. Ik zie zojuist dat model 2 en 3 hierboven een stevige tijdsverlenging met zich mee brachten. Van 38 naar 72 sec per epoch. Bij de verdubbeling van de lagen gaat de tijd naar zo'n 49 seconden.
Dat is interessant. Een verdubbeling van de layers lijkt voor een iets betere training accuracy te zorgen maar laat de validatie acc een beetje weglopen. Dat lijkt op overfitting te duiden.
Ik ga even terug naar de oorspronkelijke kearning rate (0.00001) en laat het model nu een keertje doortrainen naar 60 epochs.
Dat lijkt toch wel interesant, Naar de 66% test accuracy met een mooie F1 = 0.659. Wel lopen de curves wat uiteen en de verhoging gaat maar langaam. Ik scan eens of er andere karakter gebaseerde modellen te vinden zijn en kom op chars2vec. Die moet ik maar eens bestuderen.Ik ga even terug naar de oorspronkelijke kearning rate (0.00001) en laat het model nu een keertje doortrainen naar 60 epochs.
zondag 15 maart 2020
Leren met letters 01 (64 %) - Eerste pogingen - vaststellen datastructuur - modelvariaties
In het vorige bericht heb ik een overzicht proberen te geven van de belangrijkste voor en nadelen van het NLP leren met woorden of met letters. Om "woord bij daad" te voegen wil ik eens kijken of het mij lukt om de voordelen van beide methodes te combineren.
Als testset gebruik ik de sentiment analyse met filmrecenties die ik al eens eerder heb gebruikt. De 'simpele' uitdaging is daar om op basis van de filmrecentie te bepalen of het een positieve of een negatieve recentie betreft.
Het voordeel van deze set is dat hij uit kleine en grote letters bestaat, relatief klein is, ook relatief kleine, losse teksten met mogelijke taalfouten en dus een simpele 'binaire' vraag.
De teksten splits ik op in aparte woorden en aparte zinnen. Dit lijkt mij een voordeel te bieden omdat de woorden dan in ieder geval als aparte 'eenheden' worden beschouwd. Deze woorden bouw ik op aan de hand van de 'ord'-waarde (zeg maar ascii code).
(Ik zie later dat er ook tekens in staan met hele hoge ord waardes. Ik beperk ze tot max 256.)
Dus ik maak een 3d tensor van maximaal 10 zinnen, 30 woorden en 20 karakters per tekst. Later vereenvoudig ik de structuur door de zinnen 'samen te smelten'. (een numpy reshape van 10, 30, 20 naar 300, 20) Het gaat tenslotte vooral om de woorden afzonderlijk te herkennen.
max_sent = 10
max_words = 30
max_chars = 20
Ik ga eerst uit van het kleine, redelijk succesvolle, model voor het klassificeren vn juridische documenten. De embedding layer werkt (uiteraard?!) niet. Die haal ik weg. Ook moet ik de eind-activation omzetten van 'softmax' naar 'sigmoid' voordat het model iets zinnigs begint te doen. Na wat knutselen en experimenteren (o.a. ook nog met TimeDistributed layers) is dit een van de eerste modellen dat "redelijk" werkt:
def Model_attention_conv_best():
model = Sequential()
input_shape = (max_sent * max_words, max_chars,)
model.add(Dense(512,input_shape=(input_shape)))
model.add(Conv1D(128, 5, activation='relu'))
model.add(MaxPooling1D())
model.add(Flatten())
model.add(Dense(128))
model.add(Dense(label_size-1, activation='sigmoid'))
return model
Ik heb even de 'attention layer' uit het oorspronkelijke model verwijderd en, zoals al aangegeven, ook de embedding layer.
De trend ziet er goed uit nadat ik de learning rate eveneens omlaag heb gebracht. (De eerste experimenten stopten al na 2 a 3 epochs). De validation accuracy groeit helaas niet meer zo hard mee hetgeen op overfitting kan duiden. Ook vallen de resultaten wel wat tegen nadat ik lang heb geexperimenteerd met BERT. Maar ... het begin is er. Ik neem dit even als de 'baseline'.
Ik bouw eerst een automatische rapportage in mijn programma in zodat de gebruikte parameters en resultaten automatisch elke keer worden vastgelegd.
Omdat de selfattention layer in eerdere experimenten een stevige bijdrage gaf probeer ik deze nu weer toe te voegen.
def Model_attention_conv_bestnw():
model = Sequential()
input_shape = (max_sent * max_words, max_chars,)
model.add(Dense(512,input_shape=(input_shape)))
model.add(Conv1D(128, 5, activation='relu'))
model.add(MaxPooling1D())
model.add(SeqSelfAttention(attention_activation='sigmoid'))
model.add(Flatten())
model.add(Dense(128))
model.add(Dense(label_size-1, activation='sigmoid'))
return model
Grappig, we komen lager uit dan in het eerste model maar de validatie nauwkeurigheid groeit in ieder geval mee.
Omdat ik het model wil testen met als inspiratie het Bert model voeg ik een normalisatie layer toe die er later, bij gebruik van meerdere herhalende lagen, voor moet zorgen dat de weights niet te klein of te groot worden. Tevens maak ik de conv-layer eveneens 512 groot zodat het model genoeg ruimte houdt om nuances vast te leggen (ook Bert geinspireerd).
def Model_attention_conv():
model = Sequential()
input_shape = (max_sent * max_words, max_chars,)
model.add(Dense(512,input_shape=(input_shape)))
model.add(Conv1D(512, 5, activation='relu'))
model.add(MaxPooling1D())
model.add(SeqSelfAttention(attention_activation='sigmoid'))
model.add(BatchNormalization())
model.add(Flatten())
model.add(Dense(128))
model.add(Dense(label_size-1, activation='sigmoid'))
return model
Model: "sequential_14"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_38 (Dense) (None, 300, 512) 10752
_________________________________________________________________
conv1d_14 (Conv1D) (None, 296, 512) 1311232
_________________________________________________________________
max_pooling1d_14 (MaxPooling (None, 148, 512) 0
_________________________________________________________________
seq_self_attention_3 (SeqSel (None, 148, 512) 32833
_________________________________________________________________
batch_normalization_1 (Batch (None, 148, 512) 2048
_________________________________________________________________
flatten_13 (Flatten) (None, 75776) 0
_________________________________________________________________
dense_39 (Dense) (None, 128) 9699456
_________________________________________________________________
dense_40 (Dense) (None, 1) 129
=================================================================
Oei, dat is interessant. In plaats van stabiliteit, door de normalisatielayer, lijkt het systeem na zo'n 11 epochs instabiel te worden. Met 66 % validation accuracy op epoch 15 hebben we wel een nieuwe topper. De F1 is echter slechts 41% Ik moet nu toch even het effect weten van alleen de 512 vergroting , zonder de normalisatie layer:
Ja, dat is netjes. Veel beter zonder normalisatie. Misschien werkt dat ook pas beter bij grotere modellen. Ook deze is wat instabiel hier en daar maar veel minder en wel groeiend naar 64 % met een veel betere F1 score van 63%.
Als testset gebruik ik de sentiment analyse met filmrecenties die ik al eens eerder heb gebruikt. De 'simpele' uitdaging is daar om op basis van de filmrecentie te bepalen of het een positieve of een negatieve recentie betreft.
Het voordeel van deze set is dat hij uit kleine en grote letters bestaat, relatief klein is, ook relatief kleine, losse teksten met mogelijke taalfouten en dus een simpele 'binaire' vraag.
De teksten splits ik op in aparte woorden en aparte zinnen. Dit lijkt mij een voordeel te bieden omdat de woorden dan in ieder geval als aparte 'eenheden' worden beschouwd. Deze woorden bouw ik op aan de hand van de 'ord'-waarde (zeg maar ascii code).
(Ik zie later dat er ook tekens in staan met hele hoge ord waardes. Ik beperk ze tot max 256.)
Dus ik maak een 3d tensor van maximaal 10 zinnen, 30 woorden en 20 karakters per tekst. Later vereenvoudig ik de structuur door de zinnen 'samen te smelten'. (een numpy reshape van 10, 30, 20 naar 300, 20) Het gaat tenslotte vooral om de woorden afzonderlijk te herkennen.
max_sent = 10
max_words = 30
max_chars = 20
Ik ga eerst uit van het kleine, redelijk succesvolle, model voor het klassificeren vn juridische documenten. De embedding layer werkt (uiteraard?!) niet. Die haal ik weg. Ook moet ik de eind-activation omzetten van 'softmax' naar 'sigmoid' voordat het model iets zinnigs begint te doen. Na wat knutselen en experimenteren (o.a. ook nog met TimeDistributed layers) is dit een van de eerste modellen dat "redelijk" werkt:
def Model_attention_conv_best():
model = Sequential()
input_shape = (max_sent * max_words, max_chars,)
model.add(Dense(512,input_shape=(input_shape)))
model.add(Conv1D(128, 5, activation='relu'))
model.add(MaxPooling1D())
model.add(Flatten())
model.add(Dense(128))
model.add(Dense(label_size-1, activation='sigmoid'))
return model
Ik heb even de 'attention layer' uit het oorspronkelijke model verwijderd en, zoals al aangegeven, ook de embedding layer.
De trend ziet er goed uit nadat ik de learning rate eveneens omlaag heb gebracht. (De eerste experimenten stopten al na 2 a 3 epochs). De validation accuracy groeit helaas niet meer zo hard mee hetgeen op overfitting kan duiden. Ook vallen de resultaten wel wat tegen nadat ik lang heb geexperimenteerd met BERT. Maar ... het begin is er. Ik neem dit even als de 'baseline'.
Ik bouw eerst een automatische rapportage in mijn programma in zodat de gebruikte parameters en resultaten automatisch elke keer worden vastgelegd.
Omdat de selfattention layer in eerdere experimenten een stevige bijdrage gaf probeer ik deze nu weer toe te voegen.
def Model_attention_conv_bestnw():
model = Sequential()
input_shape = (max_sent * max_words, max_chars,)
model.add(Dense(512,input_shape=(input_shape)))
model.add(Conv1D(128, 5, activation='relu'))
model.add(MaxPooling1D())
model.add(SeqSelfAttention(attention_activation='sigmoid'))
model.add(Flatten())
model.add(Dense(128))
model.add(Dense(label_size-1, activation='sigmoid'))
return model
Grappig, we komen lager uit dan in het eerste model maar de validatie nauwkeurigheid groeit in ieder geval mee.
Omdat ik het model wil testen met als inspiratie het Bert model voeg ik een normalisatie layer toe die er later, bij gebruik van meerdere herhalende lagen, voor moet zorgen dat de weights niet te klein of te groot worden. Tevens maak ik de conv-layer eveneens 512 groot zodat het model genoeg ruimte houdt om nuances vast te leggen (ook Bert geinspireerd).
def Model_attention_conv():
model = Sequential()
input_shape = (max_sent * max_words, max_chars,)
model.add(Dense(512,input_shape=(input_shape)))
model.add(Conv1D(512, 5, activation='relu'))
model.add(MaxPooling1D())
model.add(SeqSelfAttention(attention_activation='sigmoid'))
model.add(BatchNormalization())
model.add(Flatten())
model.add(Dense(128))
model.add(Dense(label_size-1, activation='sigmoid'))
return model
Model: "sequential_14"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_38 (Dense) (None, 300, 512) 10752
_________________________________________________________________
conv1d_14 (Conv1D) (None, 296, 512) 1311232
_________________________________________________________________
max_pooling1d_14 (MaxPooling (None, 148, 512) 0
_________________________________________________________________
seq_self_attention_3 (SeqSel (None, 148, 512) 32833
_________________________________________________________________
batch_normalization_1 (Batch (None, 148, 512) 2048
_________________________________________________________________
flatten_13 (Flatten) (None, 75776) 0
_________________________________________________________________
dense_39 (Dense) (None, 128) 9699456
_________________________________________________________________
dense_40 (Dense) (None, 1) 129
=================================================================
Oei, dat is interessant. In plaats van stabiliteit, door de normalisatielayer, lijkt het systeem na zo'n 11 epochs instabiel te worden. Met 66 % validation accuracy op epoch 15 hebben we wel een nieuwe topper. De F1 is echter slechts 41% Ik moet nu toch even het effect weten van alleen de 512 vergroting , zonder de normalisatie layer:
Ja, dat is netjes. Veel beter zonder normalisatie. Misschien werkt dat ook pas beter bij grotere modellen. Ook deze is wat instabiel hier en daar maar veel minder en wel groeiend naar 64 % met een veel betere F1 score van 63%.
dinsdag 10 maart 2020
NLP overdenkingen
Het "Bert" (Bidirectional Encoder Representations from Transformers) natural language processing (nlp) model is zeer succesvol voor een heleboel nlp taken gebleken. Interessant is dat het model "(semi) unsupervised" is getraind. D.w.z. dat er geen 'labeling' van gewenste uitkomsten plaatst heeft moeten vinden. Bert heeft blijkbaar een heleboel kennis (begrip?) van taal opgestoken door op 3 dingen te oefenen:
- Het invullen van gemaskeerde woorden. 25% van de woorden zijn in de input van een 'masker' voorzien. Een specifiek label.
- Het bepalen of een eerdere zin bij een volgende zin hoort. Of dat het een onzin volgorde betreft.
- Bert maakt ook gebruik van 'woordembedding'. Dit is een, in nlp, veelgebruikte methode om woorden te definieren op basis van de woorden in de directe omgeving. Bij voldoende tekst om te leren ontstaat er een zeker 'begrip' van het betreffende woord. Het, veel gebruikte, voorbeeld is dat het woord "man" tot "koning" een gelijke relatie heeft als "vrouw" tot koningin. Je kan dus inprincipe 'uitrekenen' dat bijvoorbeeld "man" + "koning" - "koningin" = "vrouw". Uiteraard heeft het systeem nog steeds geen benul ervan wat een "vrouw" feitelijk is. Maar het woord "dame" zal er bijvoorbeeld wel in de virtuele buurt liggen.
Bert werkt, net als de meeste nlp modellen, met woorden (of woorddelen) - niet met letters. D.w.z. met indexen van woorden.
Een tekst kan er bijvoorbeeld zo uitzien:
[353, 33, 4444, 9753 ....]
De Bert 'vocabulary' (de woorden die Bert 'kent') is ongeveer 30.000 woorden groot. Als je beseft dat de "nltk" engelse woordbibliotheek uit 466.544 verschillende woorden bestaat is het duidelijk dat Bert maximaal zo'n kleine 6,5 % van de woorden kent. Dat lijkt erg weinig voor een 'top nlp' programma. Er zijn 2 manieren hoe dit bij Bert wordt opgelost:
- Bert kent vooral de meest gebruikte woorden. Dat wil wel zeggen de meest gebruikte van de data waarop Bert is getraind. (Vooral Wiki)
- Bert kent 'woorddelen'. Bijvoorbeeld 'industrie' zou kunnen bestaan uit 'indus' en '#trie'. Door ook alle letters als woorddelen op te nemen (#a .. #z, #A .. #Z) kunnen uiteindelijk alle woorden worden opgebouwd. Zij het dat elk woorddeel dus zijn eigen 'token' meekrijgt.
Wat zouden mogelijke verbeteringen in het nlp gebied kunnen zijn? Met alle slimme keuzes die hierbij gemaakt zijn gaat, vermoedelijk, wel wat "kostbare" informatie verloren:
- Hoofd / kleine letters. Bert is in 2 (engelse) versies uitgebracht (los van de verschillende model groottes). Een kleine letter variant en een gemixte variant waarin de hoofdletters, bij de tekst voorbewerking, ongemoeid zijn gelaten. Het voordeel van de 'hoofdletter variant' is dat informatie behouden blijft. "De Kok" kan gezien worden als naam en is wellicht niet het beroep. Het nadeel is dat het model, in de basis, geen enkel idee heeft dat bijvoorbeeld "De" en "de" hetzelfde woord betreft.
- Woordbasis. Bij de 'vertaling' van woorden naar getallen gaan de letters verloren. Daarmee dus ook de 'ingebouwde' betekenis. Het model heeft er bijvoorbeeld geen idee meer van dat bijvoorbeeld "brand", "brandend", "verbrand", "brandweer" een samenhangende betekenis hebben. Daarmee gaat dus ook veel potentiele betekenis verloren. In nlp wordt daarom wel gebruik gemaakt van 'stemming' of lemmetizing'. Woorden worden dan eerst teruggebracht naar hun "stam". In dit geval bijvoorbeeld "brand". Nadeel hiervan is dat daarmee ook gelijk de woordvariatie verloren gaat voor het systeem. Een "brand" en "brandweer" hebben toch ook andere betekenissen.
- Typefouten. Afhankelijk van de bron van de informatie zijn er meer of minder type- en spelfouten. Deze zullen niet vaak in die specifieke vorm terugkomen en dus al snel of een unieke 'key' krijgen of, veel waarschijnlijker, buiten de vocabulary vallen. Een woord als "meesterljik" wordt dus bijvoorbeeld niet herkend in een film- of boek recentie.
- Bijzondere woorden. Een aantal "woorden" hebben een heel specifieke, vaak herkenbare, betekenis. Een goed voorbeeld hiervan is een emailadres (xx@mymail.com) of een webadres (www.wowwateensite.nl). Het minste dat hier nodig lijkt is dat deze niet als aparte woorden in het model opgenomen worden. Vaak wordt punctuatie, in de voorbewerking, vervangen door een spatie waardoor deze 'fout' ontstaat.
- Punctuaties: !@#$%^&*.:;'() etc. Deze tekens voegen normaliter 'betekenis' toe aan een tekst. Bij gebruik in nlp worden deze vaak geheel of iig voor het grootste deel verwijderd. Voordeel is dan in ieder geval wel dat een woord hetzelfde wordt gezien met of zonder aansluitend teken. ("vooruit" is hetzelfde als "vooruit!" maar denk ook aan een zinafsluitingspunt.) Het correct behandelen van afkortingen is daarbij ook een uitdaging. Is "k.n.m.i" hetzelfde als "knmi" of "k n m i"? Je moet dan wel vooraf kunnen herkennen dat een punt gebruikt wordt in een afkorting en niet als zinseinde.
- Getallen: Hoe behandel je getallen? Vaak wordt een willekeurige selectie van getallen aan de vocabulary toegevoegd. Je kan ze als met aparte keys per 'digit' opnemen. Wat te doen met de tussenliggende decimale punten of comma's? (zie ook 'punctuaties')
- Woordlengte. Hoewel minder hard te maken, is er gevoelsmatig een relatie tussen de woordlengte en de "complexiteit" van de zin en / of het woord. Voor de woorden die naar getallen worden vertaald gaat deze informatie verloren. Wellicht heeft Bert dit probleem al een beetje ondervangen door met name de minder gebruikte woorden als aparte letters op te nemen.
- Tekstopbouw (titels / alineas / voetnoten) Een koptekst heeft een heel andere waarde in een verhaal dan de tekst zelf. Vaak wordt daar de kern weergegeven van waar de betreffende alinea's eronder over gaan. Dit lijkt mij uiterst belangrijke informatie bij een nlp vraagstuk. Denk ook bijvoorbeeld aan dikgedrukte of cursieve woorden. Meestal gaat de tekst opbouw in een nlp model verloren (hoewel een bekend ander model, gpt-2, structuur wel deels lijkt vast te houden.)
Een 'simpele' oplossing voor (de meeste van ) deze problemen lijkt te zijn door naar 'letter-modellen' toe te gaan. Modellen die gevoed worden met de letters zelf en niet met de woorden. In praktijk blijken deze modellen tot nu toe veel slechter te werken dan woord-modellen. De uitdaging is dus "Is er een model te bedenken dat "best of both" aan kan? (En de vervolg vraag uiteraard "Levert het wat op?")
Mogelijk een van de grootste gebreken van een 'letter-model' is het ontbreken van de "wordembedding", de logica die de modellen vinden door woorden te definieren aan de hand van "omgevingswoorden". Deze kennisopbouw zou je dus in ieder geval ook willen vasthouden.
Mogelijk een van de grootste gebreken van een 'letter-model' is het ontbreken van de "wordembedding", de logica die de modellen vinden door woorden te definieren aan de hand van "omgevingswoorden". Deze kennisopbouw zou je dus in ieder geval ook willen vasthouden.
dinsdag 3 maart 2020
Bertje opnieuw
Bij deze Kaggle kernel wordt gebruik gemaakt van een mini implementatie van Bert voor de 'Disaster competitie'. Dat is interessant. Er lijkt vrijwel alleen van de embeddings gebruik gemaakt te worden.
Ik snap het nog niet helemaal. Maar het model is minimaal:
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_word_ids (InputLayer) [(None, 160)] 0
__________________________________________________________________________________________________
input_mask (InputLayer) [(None, 160)] 0
__________________________________________________________________________________________________
segment_ids (InputLayer) [(None, 160)] 0
__________________________________________________________________________________________________
keras_layer (KerasLayer) [(None, 1024), (None 335141889 input_word_ids[0][0]
input_mask[0][0]
segment_ids[0][0]
__________________________________________________________________________________________________
tf_op_layer_strided_slice (Tens [(None, 1024)] 0 keras_layer[0][1]
__________________________________________________________________________________________________
dense (Dense) (None, 1) 1025 tf_op_layer_strided_slice[0][0]
==================================================================================================
Total params: 335,142,914
Trainable params: 335,142,913
Non-trainable params: 1
Daarnaast lijkt er een goede data voorbereidings module in te zitten. Ik ben daar bij mijn eerdere testen wat laks in geweest en ik ben dus benieuwd of dit tot betere resultaten kan leiden.
Ik besluit eerst mijn eerdere tests met Bertje eens met deze verbeterde input te proberen.
Na wat worstelen wordt de data goed aangemaakt krijg ik een bekende, eerder tegenaan gelopen, foutmelding:
ValueError: Error when checking model input: the list of Numpy arrays that you are passing to your model is not the size the model expected. Expected to see 3 array(s), but instead got the following list of 1 arrays: [array([[[ 1, 14640, 28738, ..., 13538, 24142, 2],
Gek genoeg geeft de nieuwe, gekopieerde functie niet de uiste dataset vorm af.
>>> train_x
array([[[ 1, 14640, 28738, ..., 13538, 24142, 2],
[ 1, 8740, 132, ..., 22538, 25138, 2],
[ 1, 14115, 121, ..., 10928, 23906, 2],
...,
>>> train_x.shape
(3, 42896, 64)
Die drie haken ([[[ ) heb ik eerder gezien. Maar hoe heb ik dat ook alweer opgelost?
Ik ga maar weer eens even graven ...
Aha, het blijkt een tuple te zijn geworden van 3 arrays. Een eenvoudige conversie naar list maakt het trainen weer mogelijk:
Spannend! ...
Nou aardig , maar we hebben het beter gedaan volgens mij:
https://dww-deeplearning.blogspot.com/2020/02/bertje-als-meer-tekst-niet-helpt.html
Ik snap het nog niet helemaal. Maar het model is minimaal:
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_word_ids (InputLayer) [(None, 160)] 0
__________________________________________________________________________________________________
input_mask (InputLayer) [(None, 160)] 0
__________________________________________________________________________________________________
segment_ids (InputLayer) [(None, 160)] 0
__________________________________________________________________________________________________
keras_layer (KerasLayer) [(None, 1024), (None 335141889 input_word_ids[0][0]
input_mask[0][0]
segment_ids[0][0]
__________________________________________________________________________________________________
tf_op_layer_strided_slice (Tens [(None, 1024)] 0 keras_layer[0][1]
__________________________________________________________________________________________________
dense (Dense) (None, 1) 1025 tf_op_layer_strided_slice[0][0]
==================================================================================================
Total params: 335,142,914
Trainable params: 335,142,913
Non-trainable params: 1
Daarnaast lijkt er een goede data voorbereidings module in te zitten. Ik ben daar bij mijn eerdere testen wat laks in geweest en ik ben dus benieuwd of dit tot betere resultaten kan leiden.
Ik besluit eerst mijn eerdere tests met Bertje eens met deze verbeterde input te proberen.
Na wat worstelen wordt de data goed aangemaakt krijg ik een bekende, eerder tegenaan gelopen, foutmelding:
ValueError: Error when checking model input: the list of Numpy arrays that you are passing to your model is not the size the model expected. Expected to see 3 array(s), but instead got the following list of 1 arrays: [array([[[ 1, 14640, 28738, ..., 13538, 24142, 2],
Gek genoeg geeft de nieuwe, gekopieerde functie niet de uiste dataset vorm af.
>>> train_x
array([[[ 1, 14640, 28738, ..., 13538, 24142, 2],
[ 1, 8740, 132, ..., 22538, 25138, 2],
[ 1, 14115, 121, ..., 10928, 23906, 2],
...,
>>> train_x.shape
(3, 42896, 64)
Die drie haken ([[[ ) heb ik eerder gezien. Maar hoe heb ik dat ook alweer opgelost?
Ik ga maar weer eens even graven ...
Aha, het blijkt een tuple te zijn geworden van 3 arrays. Een eenvoudige conversie naar list maakt het trainen weer mogelijk:
Spannend! ...
Nou aardig , maar we hebben het beter gedaan volgens mij:
https://dww-deeplearning.blogspot.com/2020/02/bertje-als-meer-tekst-niet-helpt.html
Abonneren op:
Posts (Atom)