Applicazione delle reti neurali in ambito NLP: codifica One Hot vector, esempio in Keras.
In questa serie di articoli spiegherò come utilizzare le reti neurali in ambito di NLP (Natural Language processing) per effettuare classificazione di testo, analisi della semantica del testo, generazione automatica di testi. Occorre prima di tutto chiarire che le reti neurali lavorano esclusivamente su dati numerici, per cui, risulta intuitivo capire che lavorare direttamente sul testo non è possibile. Il primo passo, pertanto, da fare, quando si vogliono utilizzare le reti neural, in ambito NLP, è quello di imparare a convertire il testo in sequenze numeriche. L’operazione di trasformazione da testo a numeri, la chiameremo d’ora in poi tokenizzazione. Di seguito proverò a spiegare gli approcci che potrebbero essere utilizzati per effettuare questo tipo di trasformazione. L’operazione di trasformazione del testo in sequenze numeriche introduce l’esigenza di utilizzare un indice o un dizionario da cui sarà possibile evincere l’associazione univoca tra carattere/parola ed ID numerico.
Conversione dei soli caratteri
Come primo tentativo, potremmo trasformare un testo in una sequenza numerica, considerando la possibilità di trasformare ogni singolo carattere in un numero ed ottenere di conseguenza l’equivalente numerico di una stringa. In questa prima ipotesi un dizionario di riferimento potrebbe essere, ad esempio, quello del codice ascii, in quanto come sappiamo esso identifica univocamente i caratteri utilizzabili su un computer. Per completezza, riporto una tabella che riassume il codice ascii dei principali caratteri stampabili.
DEC | OCT | HEX | BIN | Simbolo |
32 | 40 | 20 | 100000 | space |
33 | 41 | 21 | 100001 | ! |
34 | 42 | 22 | 100010 | “ |
35 | 43 | 23 | 100011 | # |
36 | 44 | 24 | 100100 | $ |
37 | 45 | 25 | 100101 | % |
38 | 46 | 26 | 100110 | & |
39 | 47 | 27 | 100111 | ‘ |
40 | 50 | 28 | 101000 | ( |
41 | 51 | 29 | 101001 | ) |
42 | 52 | 2A | 101010 | * |
43 | 53 | 2B | 101011 | + |
44 | 54 | 2C | 101100 | , |
45 | 55 | 2D | 101101 | – |
46 | 56 | 2E | 101110 | . |
47 | 57 | 2F | 101111 | / |
48 | 60 | 30 | 110000 | 0 |
49 | 61 | 31 | 110001 | 1 |
50 | 62 | 32 | 110010 | 2 |
51 | 63 | 33 | 110011 | 3 |
52 | 64 | 34 | 110100 | 4 |
53 | 65 | 35 | 110101 | 5 |
54 | 66 | 36 | 110110 | 6 |
55 | 67 | 37 | 110111 | 7 |
56 | 70 | 38 | 111000 | 8 |
57 | 71 | 39 | 111001 | 9 |
58 | 72 | 3A | 111010 | : |
59 | 73 | 3B | 111011 | ; |
60 | 74 | 3C | 111100 | < |
61 | 75 | 3D | 111101 | = |
62 | 76 | 3E | 111110 | > |
63 | 77 | 3F | 111111 | ? |
64 | 100 | 40 | 1000000 | @ |
65 | 101 | 41 | 1000001 | A |
66 | 102 | 42 | 1000010 | B |
67 | 103 | 43 | 1000011 | C |
68 | 104 | 44 | 1000100 | D |
69 | 105 | 45 | 1000101 | E |
70 | 106 | 46 | 1000110 | F |
71 | 107 | 47 | 1000111 | G |
72 | 110 | 48 | 1001000 | H |
73 | 111 | 49 | 1001001 | I |
74 | 112 | 4A | 1001010 | J |
75 | 113 | 4B | 1001011 | K |
76 | 114 | 4C | 1001100 | L |
77 | 115 | 4D | 1001101 | M |
78 | 116 | 4E | 1001110 | N |
79 | 117 | 4F | 1001111 | O |
80 | 120 | 50 | 1010000 | P |
81 | 121 | 51 | 1010001 | Q |
82 | 122 | 52 | 1010010 | R |
83 | 123 | 53 | 1010011 | S |
84 | 124 | 54 | 1010100 | T |
85 | 125 | 55 | 1010101 | U |
86 | 126 | 56 | 1010110 | V |
87 | 127 | 57 | 1010111 | W |
88 | 130 | 58 | 1011000 | X |
89 | 131 | 59 | 1011001 | Y |
90 | 132 | 5A | 1011010 | Z |
91 | 133 | 5B | 1011011 | [ |
92 | 134 | 5C | 1011100 | \ |
93 | 135 | 5D | 1011101 | ] |
94 | 136 | 5E | 1011110 | ^ |
95 | 137 | 5F | 1011111 | _ |
96 | 140 | 60 | 1100000 | ` |
97 | 141 | 61 | 1100001 | a |
98 | 142 | 62 | 1100010 | b |
99 | 143 | 63 | 1100011 | c |
100 | 144 | 64 | 1100100 | d |
101 | 145 | 65 | 1100101 | e |
102 | 146 | 66 | 1100110 | f |
103 | 147 | 67 | 1100111 | g |
104 | 150 | 68 | 1101000 | h |
105 | 151 | 69 | 1101001 | i |
106 | 152 | 6A | 1101010 | j |
107 | 153 | 6B | 1101011 | k |
108 | 154 | 6C | 1101100 | l |
109 | 155 | 6D | 1101101 | m |
110 | 156 | 6E | 1101110 | n |
111 | 157 | 6F | 1101111 | o |
112 | 160 | 70 | 1110000 | p |
113 | 161 | 71 | 1110001 | q |
114 | 162 | 72 | 1110010 | r |
115 | 163 | 73 | 1110011 | s |
116 | 164 | 74 | 1110100 | t |
117 | 165 | 75 | 1110101 | u |
118 | 166 | 76 | 1110110 | v |
119 | 167 | 77 | 1110111 | w |
120 | 170 | 78 | 1111000 | x |
121 | 171 | 79 | 1111001 | y |
122 | 172 | 7A | 1111010 | z |
123 | 173 | 7B | 1111011 | { |
124 | 174 | 7C | 1111100 | | |
125 | 175 | 7D | 1111101 | } |
126 | 176 | 7E | 1111110 | ~ |
127 | 177 | 7F | 1111111 | delete |
Il codice ascii ci consente di trasformare ogni singolo carattere in un numero, per essere più precisi, ogni carattere può essere rappresentato in un byte. |
Cerchiamo, di capire cosa potrebbe succedere nel caso in cui decidessimo di utilizzare la trasformazione carattere -> codice ascii, e per farlo facciamo riferimento al seguente esempio:
Il gatto appartiene al genere dei felini oppure Al genere dei felini appartiene il gatto
Leggendo le 2 frasi, risulta evidente che, sebbene siano sinteticamente diverse, non lo sono invece da un punto di vista strettamente semantico. Ciò significa che il contenuto intrinseco delle due frasi è identico. Se provassimo pertanto a trasformare le 2 frasi in sequenze di numeri utilizzando il codice ascii il risultato sarebbe qualcosa del genere:
73 108 32 103 97 116 116 111 32 97 112 112 97 114 116 105 101 110 101 32 97 108 32 103 101 110 101 114 101 32 100 101 105 32 102 101 108 105 110 105
65 108 32 103 101 110 101 114 101 32 100 101 105 32 102 101 108 105 110 105 32 97 112 112 97 114 116 105 101 110 101 32 105 108 32 103 97 116 116 111
Oppure in binario:
01001001 01101100 00100000 01100111 01100001 01110100 01110100 01101111 00100000 01100001 01110000 01110000 01100001 01110010 01110100 01101001 01100101 01101110 01100101 00100000 01100001 01101100 00100000 01100111 01100101 01101110 01100101 01110010 01100101 00100000 01100100 01100101 01101001 00100000 01100110 01100101 01101100 01101001 01101110 01101001
01000001 01101100 00100000 01100111 01100101 01101110 01100101 01110010 01100101 00100000 01100100 01100101 01101001 00100000 01100110 01100101 01101100 01101001 01101110 01101001 00100000 01100001 01110000 01110000 01100001 01110010 01110100 01101001 01100101 01101110 01100101 00100000 01101001 01101100 00100000 01100111 01100001 01110100 01110100 01101111
Dal confronto delle due sequenze numeriche ci si rende conto che sebbene le 2 frasi siano semanticamente identiche, la rappresentazione numerica delle stesse risulta estremamente differente; il ché ci pone di fronte ad un primo problema, ovvero individuare un metodo che ci possa garantire in un certo senso una rappresentazione delle frasi che mantenga anche intatto il contenuto semantico. Risulta infatti chiaro che le due sequenze non sono confrontabili e far comprendere ad una rete neurale che le stesse hanno un contenuto semantico identico può diventare estremamente oneroso.
Codifica One Hot vector
Proviamo, dunque, ad utilizzare un approccio differente, andando a codificare le parole e non i singoli caratteri. In questo caso, a differenza della trasformazione carattere -> testo, in cui disponevamo di uno dizionario standard, dobbiamo preoccuparci, prima di tutto, di costruirci un dizionario di riferimento. Riprendiamo pertanto gli esempi precedenti e cerchiamo di costruire il nostro dizionario.
Riprendiamo le due frasi:
Il gatto appartiene al genere dei felini oppure Al genere dei felini appartiene il gatto
effettuiamo la costruzione del dizionario assegnando ad ogni singola parola un ID univoco : parola -> indice. Ad esempio:
il -> 1 gatto -> 2 appartiene -> 3 al -> 4 genere -> 5 dei -> 6 felini -> 7
In tal caso le frasi diventerebbero:
Il gatto appartiene al genere dei felini -> 1 2 3 4 5 6 7 Al genere dei felini appartiene il gatto -> 4 5 6 7 3 1 2
Con questo approccio si nota chiaramente che sebbene le sequenze codificate siano diverse, si possono individuare facilmente le singole parole con lo stesso ID. Se applicassimo, ad esempio, un algoritmo di similitudine basato sulla presenza o meno delle parole contenute nelle due frasi, si troverebbe facilmente la similitudine delle stesse. Questo approccio, in definitiva, utilizza l’operazione di intersezione tra insiemi, quale metodo per calcolare la similitudine di due frasi ; infatti applicando l’operazione di intersezione su due insiemi i cui elementi sono le parole contenute nelle due frasi, si ottine che più l’intersezione si avvicina all’isimene pieno più le frasi sono simili.
N.B
Esistono metodi più efficaci per la codifica delle parole, uno tra tutti è l’ embedding word2vec, ma, di questo, ne parleremo nel prossimo articolo. |
Per il momento cerchiamo di capire come sia possibile con Keras effettuare la tokenizzazione delle frasi in poche righe di codice.
Come prima cosa dovremmo importare keras e Tokenizer
import keras
from keras.preprocessing.text import Tokenizer
Definiamo l’array delle frasi su cui costruire il dizionario.
sentence = [ 'Il gatto appartiene al genere dei felini', 'Al genere dei felini appartiene il gatto' ]
Istanziamo il Tokenizer, da tener presente che il tag <oov> identifica tutte le parole che non hanno un equivalente in dizionario: out of vocabulary.
tk = Tokenizer(num_words =100,oov_token="<oov>")
generiamo il dizionario con fit_on_texts
tk.fit_on_texts(sentence)
creiamo l’associazione word -> index generato
word_index=tk.word_index
stampiamo il dizionario, come si vede <oov> avrà indice 1. Pertanto tutte le parole non incluse nel dizionario saranno associate con id 1:
print(word_index)
{‘<oov>’: 1, ‘il’: 2, ‘gatto’: 3, ‘appartiene’: 4, ‘al’: 5, ‘genere’: 6, ‘dei’: 7, ‘felini’: 8}
sentences = []
Proviamo a tokenizzare una frase in cui inseriamo anche parole non esistenti in dizionario:
sentences.append('Il gatto è bello')
eseguiamo la tokenizzazione com texts_to_sequences
sequences = tk.texts_to_sequences(sentences)
Stampiamo la sequenza tokenizzata:
print(sequences)
[[2, 3, 1, 1]]
seconda parte in progress>>
Sono amante della tecnologia e delle tante sfumature del mondo IT, ho partecipato, sin dai primi anni di università ad importanti progetti in ambito Internet proseguendo, negli anni, allo startup, sviluppo e direzione di diverse aziende; Nei primi anni di carriera ho lavorato come consulente nel mondo dell’IT italiano, partecipando attivamente a progetti nazionali ed internazionali per realtà quali Ericsson, Telecom, Tin.it, Accenture, Tiscali, CNR. Dal 2010 mi occupo di startup mediante una delle mie società techintouch S.r.l che grazie alla collaborazione con la Digital Magics SpA, di cui sono Partner la Campania, mi occupo di supportare ed accelerare aziende del territorio .
Attualmente ricopro le cariche di :
– CTO MareGroup
– CTO Innoida
– Co-CEO in Techintouch s.r.l.
– Board member in StepFund GP SA
Manager ed imprenditore dal 2000 sono stato,
CEO e founder di Eclettica S.r.l. , Società specializzata in sviluppo software e System Integration
Partner per la Campania di Digital Magics S.p.A.
CTO e co-founder di Nexsoft S.p.A, società specializzata nella Consulenza di Servizi in ambito Informatico e sviluppo di soluzioni di System Integration, CTO della ITsys S.r.l. Società specializzata nella gestione di sistemi IT per la quale ho partecipato attivamente alla fase di startup.
Sognatore da sempre, curioso di novità ed alla ricerca di “nuovi mondi da esplorare“.
Comments