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%.








Geen opmerkingen:

Een reactie posten