WMR 12 - Lab II (Standford coreNLP + Spacy)
Lecture Info
Date:
Course Site: Web Mining e Retrieval (a.a. 2019/20)
Lecturer: Danilo Croce
Slides: ()
Table of Contents:
In questo laboratorio abbiamo visto come utilizzare le informazioni ottenute dal processo di parsing morfo-sintattico per risolvere un particolare task tramite l'utilizzo di due sistemi: Spacy e Standford CoreNLP.
1 Standford CoreNLP
Sistema open-source scritto in Java, è il risultato del migliore gruppo gruppo che si occupa di analisi morfo-sintattica. Vince quasi sistematicamente le competizioni a livello mondiale.
Alcuni link utili:
Questo sistema definisce una pipeline di processi, rappresentata dal seguente diagramma
Osserviamo come tale sistema non permette solamente di estrarre dal testo delle informazioni morfo-sintattiche, ma permette anche di inferire informazioni sul sentiment, sul gender, e su altre tipologie di informazioni che hanno a che fare più sulla semantica che sulla sintassi.
Consideriamo il seguente esempio di utilizzo, in cui vengono mostrato il PoS tagging e i lemmi.
1.1 How to Use it
Questo sistema può essere integrato in un qualsiasi progetto Java
gestito con maven andando ad aggiungere la seguente dipendenza al
file pom.xml
.
<dependencies> <dependency> <groupId>edu.stanford.nlp</groupId> <artifactId>stanford-corenlp</artifactId> <version>3.6.0</version> </dependency> <dependency> <groupId>edu.stanford.nlp</groupId> <artifactId>stanford-corenlp</artifactId> <version>3.6.0</version> <classifier>models</classifier> </dependency> </dependencies>
1.2 The CONNL Tabular Format
Una volta importato possiamo utilizzarlo come segue
Properties props = new Properties(); props.put("annotators", "tokenize, spplit, pos, lemma, ner, parse"); StanfordCoreNLP pipeline = new StanfordCoreNLP(props); String str = "In 1982, Mark drove his car from Los Angeles to Las Vegas"; Annotation document = new Annotation(str); pipeline.annotate(document); List<CoreMap> sentences = document.get(SentencesAnnotation.class); // for each sentence for (CoreMap sentence : sentences) { // dependency graph SemanticGraph dependencies = sentence.get(BasicDependenciesAnnotation.class); // For each token for (CoreLabel token : sentence.get(TokensAnnotation.class)) { IndexedWord sourceNode = dependencies.getNodeByIndex(token.index()); IndexedWord father = dependencies.getParent(sourceNode); // If no father, then we have root int fatherId = 0; String relName = "root"; if (father != null) { fatherId = father.index(); SemanticGraphEdge edge = dependencies.getEdge(father, sourceNode); relName = edge.getRelation().getShortName(); } System.out.println(token.index() + "\t" + token.originalText() + "\t" + token.lemma() + "\t" + token.tag() + "\t" + token.ner() + "\t" + relName + "\t" + fatherId); } System.out.println(); }
La forma tabellare per rappresentare il grafo alle dipendenze di una frase è detto CONLL tabular format in quanto è il formato utilizzato per rilasciare i benchmark organizzati dalla conferenza CONLL.
Per ogni riga della tabella ho le seguenti informazioni
tokenID
surface
lemma
pos
named entity
relation name
dependant
2 Spacy
Un'altra libreria per fare NLP è spacy, che è implementato in python.
2.1 Anaconda
Python ha una gestione delle librerie abbastanza peculiare, in quanto installa all'interno di una cartella tutte le librerie di cui ha bisogno. Il problema di questo approccio è che non si interessa della retro-compatibilità, e quando vengono modificate le librerie non si ha la sicurezza di essere retro-compatibili.
Per risolvere questi problemi e avere la sicurezza di installare una particolare versione di una libreria, si utilizzano i gestori dei pacchetti. In particolare si definisco degli ambienti di lavoro, detti anche environments, in modo tale che si possono utilizzare diverse versioni della stessa libreria su diversi environment.
Un esempio di gestore dei pacchetti è anaconda, che permette la gestione di ambienti di sviluppo python, ciascuno con le proprie dipendenze. Per creare un nuovo ambiente di sviluppo con anaconda possiamo utilizzare i seguenti comandi
conda create -n spacyenv conda activate spacyenv conda install spacy python -m spacy download en
2.2 Basic Example
Andiamo a stampare le informazioni che abbiamo visto prima utilizzando la libreria StanfordNLP in Java.
import spacy nlp = spacy.load('en') doc = nlp('In 1982, Mark drove his car from Los Angeles to Las Vegas') for sent in doc.sents: for i, word in enumerate(sent): if word.head is word: head_idx = 0 else: head_idx = doc[i].head.i + 1 if head_idx == i + 1: head_idx = 0 entity_tag = word.ent_type_ if len(entity_tag) == 0: entity_tag = "O" print("%d\t%s\t%s\t%s\t%s\t%s\t%d" % (i + 1, word, word.lemma_, word.tag_, entity_tag, word.dep_, head_idx))
A questo punto possiamo utilizzare le informazioni morfo-sintattiche ottenute dal parser Spacy per andare ad effettuare vari tasks. Tra questi task possiamo considerare i seguenti:
Derivare features per arricchire la rappresentazione per un classificatore di tipo Naive Bayes.
Estrarre tutte le triple della forma (soggetto, verbo, oggetto diretto) da una collezione documentale (es: parole di Wikipedia).
3 Example (Wikipedia words)
Supponiamo di avere una serie di frasi riprese da wikipedia e parsate da coreNLP nel seguente modo
1 In in IN O case 2 2 1963 1963 CD DATE nmod 5 3 Joseph Joseph NNP PERSON compound 4 4 Greenberg Greenberg NNP PERSON nsubj 5 5 added add VBD O root 0 6 them they PRP O dobj 5 7 to to TO O case 9 8 the the DT O det 9 9 Niger Niger NNP LOCATION nmod 5 10 – -- : O punct 9 11 Congo Congo NNP LOCATION compound 12 12 family family NN O dep 9 13 , , , O punct 5 14 creating create VBG O advcl 5 15 his he PRP$ O nmod:poss 16 16 Niger Niger NNP LOCATION dobj 14 17 – -- : O punct 16 18 Kordofanian kordofanian JJ MISC amod 19 19 proposal proposal NN O dep 16 20 . . . O punct 5
dove come le varie colonne stanno per (in ordine)
index, surface, lemma, pos, named entity, relation name, dependant
3.1 Extracting Triples (Subject, Verb, Object)
Inizialmente posso quindi pensare di estrarre solamente le triple
(soggetto, verbo, oggetto). Utilizzando lo script
CoreNLP_subj-obj_pattern_extractor.py
ottengo il seguente output.
3.2 Generalizing
Un primo approccio per generalizzare potrebbe essere quello di processare l'output dello script precedente con bash nel seguente modo
python CoreNLP_subj-obj_pattern_extractor.py ../wiki_en_20150114_parsed_first_1M.txt.gz | sort | uniq -c | sort -k 1
Volendo generalizzare meglio però possiamo utilizzare il named entity reconigized, e sostituire alle particolare istanze la loro named entity.
python CoreNLP_subj-obj_pattern_extractor_NE.py ../wiki_en_20150114_parsed_first_1M.txt.gz | sort | uniq -c | sort -k 1
4 Exercise: Q/A
Nel precedente laboratorio avevamo visto come in weka potevamo
utilizzare la funzione String2WordVector
per tokenizzare la frase ed
assegnare dei pesi ad ogni token.
L'idea adesso è quella di riscrivere l'arff file andando a codificare ogni stringa come un vettore in cui le singole dimensioni sono le parole ottenute applicando il tokenizzatore di spacy.
Se al posto della parola metto il lemma, sto efettuando feature redaction.
Come posso utilizzare le informazioni sintattiche per aiutarmi nel processo di classificazione delle frasi? L'idea è quella di utilizare i bigrammi al posto degli unigrammi. In questo modo siamo in grado di iniettare piccole ma importanti informazioni sintattiche nel nostro modello.
Sfida: utilizzare spacy per costruire l'arf file utilizzando la rappresentazione vista a lezione, in cui dobbiamo:
Capire il dizionario dei simboli leggendo tutto quanto il training set.
Ad ogni indice della parola diamo un peso stando attenti al primo simbolo, che è legato alla classe. La prima pesatura può essere binaria e fatta sugli unigrammi, mentre la seconda pesatura può essere binaria e fatta sui bigrammi. Inoltre è anche possibile implementare una pesatura di tipo tf-idf.
NOTA: Nell'output prodotto da Weka una classe, la prima, non viene scritta in modo esplicito ma è intesa in modo implicito.
La Heap's law può essere utilizzata per stimare il size del dizionario rispetto al numero di token nella collezione.
Per cercare di gestire lo spazio di rappresentazione quando il dizionario è molto grande si utilizzano delle rappresentazioni implicite che permettono di gestire la dimensione dei dizionari. (kernel methods).
BERT non rappresenta le parole, ma dei pezzi di parole (tra parole e simboli), per cercare di migliorare le performance.
Q: Come aggiungiamo le informazioni sul POS tagging nella rappresentazioni delle domande?
R: L'idea è quella di sostiuire ogni parola con la struttura