Big Picture Machine Learning: classificazione del testo con reti neurali e TensorFlow

Gli sviluppatori dicono spesso che se vuoi iniziare con l'apprendimento automatico, dovresti prima imparare come funzionano gli algoritmi. Ma la mia esperienza mostra il contrario.

Dico che dovresti prima essere in grado di vedere il quadro generale: come funzionano le applicazioni. Una volta capito questo, diventa molto più facile immergersi in profondità ed esplorare il funzionamento interno degli algoritmi.

Quindi, come si sviluppa un'intuizione e si ottiene questa comprensione generale dell'apprendimento automatico? Un buon modo per farlo è creare modelli di apprendimento automatico.

Supponendo che non sai ancora come creare da zero tutti questi algoritmi, ti consigliamo di utilizzare una libreria che abbia già tutti questi algoritmi implementati per te. E quella libreria è TensorFlow.

In questo articolo, creeremo un modello di apprendimento automatico per classificare i testi in categorie. Tratteremo i seguenti argomenti:

  1. Come funziona TensorFlow
  2. Che cos'è un modello di apprendimento automatico
  3. Cos'è una rete neurale
  4. Come apprende la rete neurale
  5. Come manipolare i dati e passarli agli input della rete neurale
  6. Come eseguire il modello e ottenere i risultati della previsione

Probabilmente imparerai molte cose nuove, quindi cominciamo!

tensorflow

TensorFlow è una libreria open source per l'apprendimento automatico, creata per la prima volta da Google. Il nome della libreria ci aiuta a capire come lavoriamo con essa: i tensori sono array multidimensionali che scorrono attraverso i nodi di un grafico.

tf.Graph

Ogni calcolo in TensorFlow è rappresentato come un grafico del flusso di dati. Questo grafico ha due elementi:

  • un insieme di tf.Operation, che rappresenta le unità di calcolo
  • un set di tf.Tensor, che rappresenta le unità di dati

Per vedere come funziona tutto ciò creerai questo grafico del flusso di dati:

Un grafico che calcola x + y

Definirai x = [1,3,6] e y = [1,1,1]. Mentre il grafico funziona con tf.Tensor per rappresentare unità di dati, creerai tensori costanti:

import tensorflow come tf
x = tf.constant ([1,3,6])
y = tf.constant ([1,1,1])

Ora definirai l'unità operativa:

import tensorflow come tf
x = tf.constant ([1,3,6])
y = tf.constant ([1,1,1])
op = tf.add (x, y)

Hai tutti gli elementi del grafico. Ora devi costruire il grafico:

import tensorflow come tf
my_graph = tf.Graph ()
con my_graph.as_default ():
    x = tf.constant ([1,3,6])
    y = tf.constant ([1,1,1])
    op = tf.add (x, y)

Ecco come funziona il flusso di lavoro TensorFlow: prima crei un grafico e solo allora puoi fare i calcoli ("eseguendo" davvero i nodi del grafico con le operazioni). Per eseguire il grafico devi creare una sessione tf.

tf.Session

Un oggetto tf.Session incapsula l'ambiente in cui vengono eseguiti gli oggetti Operation e gli oggetti Tensor vengono valutati (dai documenti). Per fare ciò, dobbiamo definire quale grafico verrà utilizzato nella sessione:

import tensorflow come tf
my_graph = tf.Graph ()
con tf.Session (graph = my_graph) come sess:
    x = tf.constant ([1,3,6])
    y = tf.constant ([1,1,1])
    op = tf.add (x, y)

Per eseguire le operazioni, utilizzerai il metodo tf.Session.run (). Questo metodo esegue un "passo" del calcolo di TensorFlow, eseguendo il frammento di grafico necessario per eseguire tutti gli oggetti Operazione e valutare ogni Tensore passato nei recuperi degli argomenti. Nel tuo caso eseguirai un passaggio delle operazioni di somma:

import tensorflow come tf
my_graph = tf.Graph ()
con tf.Session (graph = my_graph) come sess:
    x = tf.constant ([1,3,6])
    y = tf.constant ([1,1,1])
    op = tf.add (x, y)
    risultato = sess.run (recupera = op)
    stampa (risultato)
>>> [2 4 7]

Un modello predittivo

Ora che sai come funziona TensorFlow, devi imparare come creare un modello predittivo. In breve,

Algoritmo di apprendimento automatico + dati = modello predittivo

Il processo per costruire un modello è così:

Il processo per creare un modello predittivo

Come puoi vedere, il modello consiste in un algoritmo di apprendimento automatico "addestrato" con i dati. Quando hai il modello otterrai risultati come questo:

Flusso di lavoro di previsione

L'obiettivo del modello che creerai è classificare i testi in categorie, definiamo che:

input: testo, risultato: categoria

Abbiamo un set di dati di formazione con tutti i testi etichettati (ogni testo ha un'etichetta che indica a quale categoria appartiene). Nell'apprendimento automatico questo tipo di attività è denominata Apprendimento supervisionato.

“Conosciamo le risposte corrette. L'algoritmo fa iterativamente previsioni sui dati di allenamento ed è corretto dall'insegnante. ”- Jason Brownlee

Classificherai i dati in categorie, quindi è anche un'attività di classificazione.

Per creare il modello, utilizzeremo le reti neurali.

Reti neurali

Una rete neurale è un modello computazionale (un modo per descrivere un sistema usando linguaggio matematico e concetti matematici). Questi sistemi sono autoapprendimento e addestrati, piuttosto che esplicitamente programmati.

Le reti neurali sono ispirate dal nostro sistema nervoso centrale. Hanno nodi collegati simili ai nostri neuroni.

Una rete neurale

Il Perceptron è stato il primo algoritmo di rete neurale. Questo articolo spiega davvero bene il funzionamento interiore di un percettrone (l'animazione "All'interno di un neurone artificiale" è fantastica).

Per capire come funziona una rete neurale realizzeremo effettivamente un'architettura di rete neurale con TensorFlow. Questa architettura è stata usata da Aymeric Damien in questo esempio.

Architettura della rete neurale

La rete neurale avrà 2 livelli nascosti (devi scegliere quanti strati nascosti avrà la rete, fa parte del progetto dell'architettura). Il lavoro di ogni livello nascosto è trasformare gli input in qualcosa che il layer di output può usare.

Livello nascosto 1

Livello di input e 1 ° livello nascosto

Devi anche definire quanti nodi avrà il primo strato nascosto. Questi nodi sono anche chiamati caratteristiche o neuroni e nell'immagine sopra sono rappresentati da ciascun cerchio.

Nel livello di input ogni nodo corrisponde a una parola del set di dati (vedremo come funzionerà in seguito).

Come spiegato qui, ogni nodo (neurone) viene moltiplicato per un peso. Ogni nodo ha un valore di peso e durante la fase di allenamento la rete neurale regola questi valori per produrre un risultato corretto (aspetta, impareremo di più su questo tra un minuto).

Oltre a moltiplicare ciascun nodo di input per un peso, la rete aggiunge anche un bias (ruolo del bias nelle reti neurali).

Nella tua architettura dopo aver moltiplicato gli input per i pesi e sommato i valori al bias, i dati passano anche per una funzione di attivazione. Questa funzione di attivazione definisce l'output finale di ciascun nodo. Un'analogia: immagina che ogni nodo sia una lampada, la funzione di attivazione dice se la lampada si accenderà o meno.

Esistono molti tipi di funzioni di attivazione. Utilizzerai l'unità lineare rettificata (ReLu). Questa funzione è definita in questo modo:

f (x) = max (0, x) [l'uscita è x o 0 (zero), a seconda di quale sia maggiore]

Esempi: ifx = -1, quindi f (x) = 0 (zero); se x = 0,7, quindi f (x) = 0,7.

Livello nascosto 2

Il 2 ° livello nascosto fa esattamente quello che fa il 1 ° livello nascosto, ma ora l'input del 2 ° livello nascosto è l'output del 1 ° livello.

1 ° e 2 ° strato nascosto

Strato di output

E finalmente siamo arrivati ​​all'ultimo livello, il livello di output. Utilizzerai la codifica one-hot per ottenere i risultati di questo livello. In questa codifica solo un bit ha il valore 1 e tutti gli altri hanno un valore zero. Ad esempio, se vogliamo codificare tre categorie (sport, spazio e computer grafica):

+ ------------------- + ----------- +
| categoria | valore |
+ ------------------- | ----------- +
| sport | 001 |
| spazio | 010 |
| computer grafica | 100 |
| ------------------- | ----------- |

Quindi il numero di nodi di output è il numero di classi del set di dati di input.

Anche i valori del livello di output vengono moltiplicati per i pesi e aggiungiamo anche il bias, ma ora la funzione di attivazione è diversa.

Vuoi etichettare ogni testo con una categoria e queste categorie si escludono a vicenda (un testo non appartiene a due categorie contemporaneamente). Per considerarlo, invece di utilizzare la funzione di attivazione ReLu, utilizzerai la funzione Softmax. Questa funzione trasforma l'output di ogni unità in un valore compreso tra 0 e 1 e si assicura anche che la somma di tutte le unità sia uguale a 1. In questo modo l'output ci dirà la probabilità di ogni testo per ogni categoria.

| 1,2 0,46 |
| 0.9 -> [softmax] -> 0.34 |
| 0.4 0.20 |

E ora hai il grafico del flusso di dati della tua rete neurale. Traducendo tutto ciò che abbiamo visto finora in codice, il risultato è:

# Parametri di rete
n_hidden_1 = 10 # Numero 1 ° livello di funzionalità
n_hidden_2 = 5 # Numero 2 ° livello di funzionalità
n_input = total_words # Parole nel vocabolario
n_classes = 3 # Categorie: grafica, spazio e baseball
def multilayer_perceptron (input_tensor, pesi, distorsioni):
    layer_1_multiplication = tf.matmul (input_tensor, weights ['h1'])
    layer_1_addition = tf.add (layer_1_multiplication, biases ['b1'])
    layer_1_activation = tf.nn.relu (layer_1_addition)
# Livello nascosto con attivazione RELU
    layer_2_multiplication = tf.matmul (layer_1_activation, pesi ['h2'])
    layer_2_addition = tf.add (layer_2_multiplication, biases ['b2'])
    layer_2_activation = tf.nn.relu (layer_2_addition)
# Output layer con attivazione lineare
    out_layer_multiplication = tf.matmul (layer_2_activation, weights ['out'])
    out_layer_addition = out_layer_multiplication + biases ['out']
return out_layer_addition

(Parleremo del codice per la funzione di attivazione del livello di output in seguito).

Come apprende la rete neurale

Come abbiamo visto in precedenza, i valori di peso vengono aggiornati durante l'allenamento della rete. Ora vedremo come ciò avvenga nell'ambiente TensorFlow.

tf.Variable

I pesi e le inclinazioni sono memorizzati in variabili (tf.Variable). Queste variabili mantengono lo stato nel grafico attraverso le chiamate a run (). Nell'apprendimento automatico di solito iniziamo i valori di peso e di inclinazione attraverso una distribuzione normale.

pesi = {
    'h1': tf.Variable (tf.random_normal ([n_input, n_hidden_1])),
    'h2': tf.Variable (tf.random_normal ([n_hidden_1, n_hidden_2])),
    'out': tf.Variable (tf.random_normal ([n_hidden_2, n_classes]))
}
pregiudizi = {
    'b1': tf.Variable (tf.random_normal ([n_hidden_1])),
    'b2': tf.Variable (tf.random_normal ([n_hidden_2])),
    'out': tf.Variable (tf.random_normal ([n_classes]))
}

Quando eseguiamo la rete per la prima volta (ovvero, i valori di peso sono quelli definiti dalla distribuzione normale):

valori di input: x
pesi: w
bias: b
valori di uscita: z
valori previsti: previsti

Per sapere se la rete sta imparando o meno, è necessario confrontare i valori di output (z) con i valori previsti (previsti). E come calcoliamo questa differenza (perdita)? Esistono molti metodi per farlo. Poiché stiamo lavorando con un'attività di classificazione, la misura migliore per la perdita è l'errore tra le entropie.

James D. McCaffrey ha scritto una brillante spiegazione sul perché questo è il metodo migliore per questo tipo di attività.

Con TensorFlow calcolerai l'errore cross-entropia usando il metodo tf.nn.softmax_cross_entropy_with_logits () (qui è la funzione di attivazione softmax) e calcolerai l'errore medio (tf.reduce_mean ()).

# Costruisci modello
prediction = multilayer_perceptron (input_tensor, pesi, distorsioni)
# Definisci la perdita
entropy_loss = tf.nn.softmax_cross_entropy_with_logits (logits = previsione, etichette = output_tensor)
perdita = tf.reduce_mean (entropy_loss)

Volete trovare i migliori valori per pesi e distorsioni al fine di minimizzare l'errore di output (la differenza tra il valore che abbiamo ottenuto e il valore corretto). Per fare ciò, utilizzerai il metodo di discesa gradiente. Per essere più specifici, utilizzerai la discesa del gradiente stocastico.

Discesa gradiente. Fonte: https://sebastianraschka.com/faq/docs/closed-form-vs-gd.html

Ci sono anche molti algoritmi per calcolare la discesa del gradiente, utilizzerai la stima del momento adattivo (Adam). Per utilizzare questo algoritmo in TensorFlow è necessario passare il valore learning_rate, che determina i passi incrementali dei valori per trovare i migliori valori di peso.

Il metodo tf.train.AdamOptimizer (learning_rate) .minimize (perdita) è uno zucchero sintattico che fa due cose:

  1. compute_gradients (perdita, )
  2. apply_gradients ()

Il metodo aggiorna tutte le variabili tf.Variable con i nuovi valori, quindi non è necessario passare l'elenco delle variabili. E ora hai il codice per addestrare la rete:

learning_rate = 0,001
# Costruisci modello
prediction = multilayer_perceptron (input_tensor, pesi, distorsioni)
# Definisci la perdita
entropy_loss = tf.nn.softmax_cross_entropy_with_logits (logits = previsione, etichette = output_tensor)
perdita = tf.reduce_mean (entropy_loss)
optimizer = tf.train.AdamOptimizer (learning_rate = learning_rate) .minimize (perdita)

Manipolazione di dati

Il set di dati che userete ha molti testi in inglese e dobbiamo manipolare questi dati per passarli alla rete neurale. Per fare ciò, farai due cose:

  1. Crea un indice per ogni parola
  2. Crea una matrice per ogni testo, dove i valori sono 1 se una parola è nel testo e 0 se no

Vediamo il codice per comprendere questo processo:

import numpy as np #numpy è un pacchetto per il calcolo scientifico
Contatore importazione collezioni
vocab = Counter ()
text = "Ciao dal Brasile"
# Ottieni tutte le parole
per word in text.split (''):
    vocab [word] + 1 =
        
# Converti le parole in indici
def get_word_2_index (vocab):
    word2index = {}
    per i, word in enumerate (vocab):
        word2index [word] = i
        
    restituisce word2index
#Ora abbiamo un indice
word2index = get_word_2_index (vocab)
total_words = len (vocab)
# Ecco come creiamo un array intorpidito (la nostra matrice)
matrix = np.zeros ((total_words), dtype = float)
#Ora riempiamo i valori
per word in text.split ():
    matrice [word2index [word]] + = 1
stampa (matrice)
>>> [1. 1. 1.]

Nell'esempio sopra il testo era "Ciao dal Brasile" e la matrice era [1. 1. 1.]. E se il testo fosse solo "Ciao"?

matrix = np.zeros ((total_words), dtype = float)
text = "Ciao"
per word in text.split ():
    matrice [word2index [word.lower ()]] + = 1
stampa (matrice)
>>> [1. 0. 0.]

Farai lo stesso con le etichette (categorie dei testi), ma ora utilizzerai la codifica one-hot:

y = np.zeros ((3), dtype = float)
se categoria == 0:
    y [0] = 1. # [1. 0. 0.]
categoria elif == 1:
    y [1] = 1. # [0. 1. 0.]
altro:
     y [2] = 1. # [0. 0. 1.]

Esecuzione del grafico e ottenimento dei risultati

Ora arriva la parte migliore: ottenere i risultati dal modello. Innanzitutto diamo un'occhiata più da vicino al set di dati di input.

Il set di dati

Utilizzerai i 20 newsgroup, un set di dati con 18.000 post su 20 argomenti. Per caricare questo set di dati utilizzerai la libreria scikit-learn. Useremo solo 3 categorie: comp.graphics, sci.space e rec.sport.baseball. Scikit-learn ha due sottoinsiemi: uno per l'allenamento e uno per i test. Il consiglio è di non guardare mai i dati del test, perché ciò può interferire con le tue scelte durante la creazione del modello. Non vuoi creare un modello per prevedere questi dati di test specifici, vuoi creare un modello con una buona generalizzazione.

Ecco come caricare i set di dati:

da sklearn.datasets importa fetch_20newsgroups
categorie = ["comp.graphics", "sci.space", "rec.sport.baseball"]
newsgroups_train = fetch_20newsgroups (sottoinsieme = 'treno', categorie = categorie)
newsgroups_test = fetch_20newsgroups (subset = 'test', categorie = categorie)

Addestramento del modello

Nella terminologia della rete neurale, un'epoca = un passaggio in avanti (ottenendo i valori di uscita) e un passaggio all'indietro (aggiornando i pesi) di tutti gli esempi di addestramento.

Ricorda il metodo tf.Session.run ()? Diamo un'occhiata più da vicino:

tf.Session.run (fetches, feed_dict = None, options = None, run_metadata = None)

Nel grafico del flusso di dati all'inizio di questo articolo hai utilizzato l'operazione di somma, ma possiamo anche passare un elenco di cose da eseguire. In questa rete neurale passerai due cose: il calcolo delle perdite e la fase di ottimizzazione.

Il parametro feed_dict è dove passiamo i dati per ogni fase della corsa. Per trasmettere questi dati dobbiamo definire tf.placeholder (per alimentare feed_dict).

Come dice la documentazione di TensorFlow:

"Un segnaposto esiste esclusivamente per fungere da bersaglio per i feed. Non è inizializzato e non contiene dati. "- Fonte

Quindi definirai i tuoi segnaposto in questo modo:

n_input = total_words # Parole nel vocabolario
n_classes = 3 # Categorie: grafica, sci.space e baseball
input_tensor = tf.placeholder (tf.float32, [None, n_input], name = "input")
output_tensor = tf.placeholder (tf.float32, [None, n_classes], name = "output")

Separerai i dati di allenamento in lotti:

"Se si utilizzano segnaposto per alimentare l'input, è possibile specificare una dimensione batch variabile creando il segnaposto con tf.placeholder (..., shape = [None, ...]). L'elemento None della forma corrisponde a una dimensione di dimensione variabile. ”- Sorgente

Alimenteremo il dict con un batch più grande durante il test del modello, ecco perché è necessario definire una dimensione batch variabile.

La funzione get_batches () ci fornisce il numero di testi con le dimensioni del batch. E ora possiamo eseguire il modello:

training_epochs = 10
# Avvia il grafico
con tf.Session () come sess:
    sess.run (init) #inserisce le variabili (distribuzione normale, ricordi?)
    # Ciclo di allenamento
    per epoca nel range (training_epochs):
        avg_cost = 0.
        total_batch = int (len (newsgroups_train.data) / batch_size)
        # Passa sopra tutti i lotti
        per i nell'intervallo (total_batch):
            batch_x, batch_y = get_batch (newsgroups_train, i, batch_size)
            # Esegui op di ottimizzazione (backprop) e costo (per ottenere il valore di perdita)
            c, _ = sess.run ([perdita, ottimizzatore], feed_dict = {input_tensor: batch_x, output_tensor: batch_y})

Ora hai il modello, addestrato. Per provarlo, dovrai anche creare elementi grafici. Misureremo l'accuratezza del modello, quindi è necessario ottenere l'indice del valore previsto e l'indice del valore corretto (poiché stiamo utilizzando la codifica one-hot), verificare che siano uguali e calcolare la media per tutti i set di dati di prova:

    # Modello di test
    index_prediction = tf.argmax (previsione, 1)
    index_correct = tf.argmax (output_tensor, 1)
    correct_prediction = tf.equal (index_prediction, index_correct)
    # Calcola la precisione
    accuratezza = tf.reduce_mean (tf.cast (correct_prediction, "float"))
    total_test_data = len (newsgroups_test.target)
    batch_x_test, batch_y_test = get_batch (newsgroups_test, 0, total_test_data)
    print ("Precisione:", accurate.eval ({input_tensor: batch_x_test, output_tensor: batch_y_test}))
>>> Epoca: perdita 0001 = 1133.908114347
    Epoca: perdita 0002 = 329.093700409
    Epoca: perdita 0003 = 111,876660109
    Epoca: perdita 0004 = 72.552971845
    Epoca: perdita 0005 = 16,673050320
    Epoca: perdita 0006 = 16.481995190
    Epoca: perdita 0007 = 4.848220565
    Epoca: perdita 0008 = 0,759822878
    Epoca: perdita 0009 = 0,000000000
    Epoca: 0010 perdita = 0,079848485
    Ottimizzazione terminata!
    Precisione: 0,75

E questo è tutto! Hai creato un modello usando una rete neurale per classificare i testi in categorie. Congratulazioni!

Puoi vedere il notebook con il codice finale qui.

Suggerimento: modifica i valori che abbiamo definito per vedere in che modo le modifiche influiscono sul tempo di allenamento e sulla precisione del modello.

Hai domande o suggerimenti? Lasciali nei commenti. Oh, e grazie per aver letto!

Hai trovato utile questo articolo? Faccio del mio meglio per scrivere un articolo di approfondimento ogni mese, puoi ricevere un'e-mail quando ne pubblico uno nuovo.

Significherebbe molto se fai clic su e lo condividi con gli amici. Seguimi per ulteriori articoli su Data Science e Machine Learning.