Incorporamenti di parole

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza l'origine su GitHub Scarica quaderno

Questo tutorial contiene un'introduzione agli incorporamenti di parole. Addestrerai i tuoi incorporamenti di parole utilizzando un semplice modello Keras per un'attività di classificazione dei sentimenti, quindi li visualizzerai nel Proiettore di incorporamento (mostrato nell'immagine sotto).

Screenshot del proiettore di incorporamento

Rappresentazione del testo come numeri

I modelli di apprendimento automatico prendono vettori (array di numeri) come input. Quando si lavora con il testo, la prima cosa da fare è elaborare una strategia per convertire le stringhe in numeri (o per "vettorizzare" il testo) prima di inviarlo al modello. In questa sezione, esaminerai tre strategie per farlo.

Codifiche one-hot

Come prima idea, potresti codificare "one-hot" ogni parola nel tuo vocabolario. Considera la frase "Il gatto si sedette sulla stuoia". Il vocabolario (o parole uniche) in questa frase è (cat, mat, on, sat, the). Per rappresentare ogni parola, creerai un vettore zero con lunghezza uguale al vocabolario, quindi ne posizionerai uno nell'indice che corrisponde alla parola. Questo approccio è mostrato nel diagramma seguente.

Diagramma delle codifiche one-hot

Per creare un vettore che contenga la codifica della frase, puoi quindi concatenare i vettori one-hot per ogni parola.

Codifica ogni parola con un numero univoco

Un secondo approccio che potresti provare è codificare ogni parola usando un numero univoco. Continuando l'esempio sopra, puoi assegnare 1 a "cat", 2 a "mat" e così via. Potresti quindi codificare la frase "Il gatto si sedette sul tappeto" come un vettore denso come [5, 1, 4, 3, 5, 2]. Questo approccio è efficiente. Invece di un vettore sparso, ora ne hai uno denso (dove tutti gli elementi sono pieni).

Ci sono due aspetti negativi di questo approccio, tuttavia:

  • La codifica dei numeri interi è arbitraria (non cattura alcuna relazione tra le parole).

  • Una codifica intera può essere difficile da interpretare per un modello. Un classificatore lineare, ad esempio, apprende un singolo peso per ciascuna caratteristica. Poiché non esiste alcuna relazione tra la somiglianza di due parole qualsiasi e la somiglianza delle loro codifiche, questa combinazione di peso delle caratteristiche non è significativa.

Incorporamenti di parole

Gli incorporamenti di parole ci danno un modo per usare una rappresentazione efficiente e densa in cui parole simili hanno una codifica simile. È importante sottolineare che non è necessario specificare manualmente questa codifica. Un incorporamento è un vettore denso di valori in virgola mobile (la lunghezza del vettore è un parametro specificato). Invece di specificare manualmente i valori per l'incorporamento, sono parametri addestrabili (pesi appresi dal modello durante l'addestramento, allo stesso modo in cui un modello apprende i pesi per uno strato denso). È comune vedere incorporamenti di parole a 8 dimensioni (per insiemi di dati di piccole dimensioni), fino a 1024 dimensioni quando si lavora con insiemi di dati di grandi dimensioni. Un'inclusione dimensionale superiore può acquisire relazioni a grana fine tra le parole, ma richiede più dati per l'apprendimento.

Schema di un incorporamento

Sopra c'è un diagramma per l'incorporamento di una parola. Ogni parola è rappresentata come un vettore quadridimensionale di valori in virgola mobile. Un altro modo di pensare a un incorporamento è come "tabella di ricerca". Dopo aver appreso questi pesi, puoi codificare ogni parola cercando il vettore denso a cui corrisponde nella tabella.

Impostare

import io
import os
import re
import shutil
import string
import tensorflow as tf

from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, Embedding, GlobalAveragePooling1D
from tensorflow.keras.layers import TextVectorization

Scarica il set di dati IMDb

Utilizzerai il set di dati di revisione di film di grandi dimensioni durante il tutorial. Addestrerai un modello di classificazione dei sentimenti su questo set di dati e nel processo imparerai gli incorporamenti da zero. Per ulteriori informazioni sul caricamento di un set di dati da zero, vedere l' esercitazione sul caricamento del testo .

Scarica il set di dati utilizzando l'utilità file Keras e dai un'occhiata alle directory.

url = "https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz"

dataset = tf.keras.utils.get_file("aclImdb_v1.tar.gz", url,
                                  untar=True, cache_dir='.',
                                  cache_subdir='')

dataset_dir = os.path.join(os.path.dirname(dataset), 'aclImdb')
os.listdir(dataset_dir)
Downloading data from https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
84131840/84125825 [==============================] - 7s 0us/step
84140032/84125825 [==============================] - 7s 0us/step
['test', 'imdb.vocab', 'imdbEr.txt', 'train', 'README']

Dai un'occhiata alla directory train/ . Ha cartelle pos e neg con recensioni di film etichettate rispettivamente come positive e negative. Utilizzerai le recensioni dalle cartelle pos e neg per addestrare un modello di classificazione binaria.

train_dir = os.path.join(dataset_dir, 'train')
os.listdir(train_dir)
['urls_pos.txt',
 'urls_unsup.txt',
 'urls_neg.txt',
 'pos',
 'unsup',
 'unsupBow.feat',
 'neg',
 'labeledBow.feat']

La directory train ha anche cartelle aggiuntive che devono essere rimosse prima di creare il set di dati di addestramento.

remove_dir = os.path.join(train_dir, 'unsup')
shutil.rmtree(remove_dir)

Quindi, crea un tf.data.Dataset usando tf.keras.utils.text_dataset_from_directory . Puoi leggere ulteriori informazioni sull'utilizzo di questa utilità in questo tutorial sulla classificazione del testo .

Usa la directory del train per creare set di dati di treno e di convalida con una suddivisione del 20% per la convalida.

batch_size = 1024
seed = 123
train_ds = tf.keras.utils.text_dataset_from_directory(
    'aclImdb/train', batch_size=batch_size, validation_split=0.2,
    subset='training', seed=seed)
val_ds = tf.keras.utils.text_dataset_from_directory(
    'aclImdb/train', batch_size=batch_size, validation_split=0.2,
    subset='validation', seed=seed)
Found 25000 files belonging to 2 classes.
Using 20000 files for training.
Found 25000 files belonging to 2 classes.
Using 5000 files for validation.

Dai un'occhiata ad alcune recensioni di film e alle loro etichette (1: positive, 0: negative) dal set di dati del treno.

for text_batch, label_batch in train_ds.take(1):
  for i in range(5):
    print(label_batch[i].numpy(), text_batch.numpy()[i])
0 b"Oh My God! Please, for the love of all that is holy, Do Not Watch This Movie! It it 82 minutes of my life I will never get back. Sure, I could have stopped watching half way through. But I thought it might get better. It Didn't. Anyone who actually enjoyed this movie is one seriously sick and twisted individual. No wonder us Australians/New Zealanders have a terrible reputation when it comes to making movies. Everything about this movie is horrible, from the acting to the editing. I don't even normally write reviews on here, but in this case I'll make an exception. I only wish someone had of warned me before I hired this catastrophe"
1 b'This movie is SOOOO funny!!! The acting is WONDERFUL, the Ramones are sexy, the jokes are subtle, and the plot is just what every high schooler dreams of doing to his/her school. I absolutely loved the soundtrack as well as the carefully placed cynicism. If you like monty python, You will love this film. This movie is a tad bit "grease"esk (without all the annoying songs). The songs that are sung are likable; you might even find yourself singing these songs once the movie is through. This musical ranks number two in musicals to me (second next to the blues brothers). But please, do not think of it as a musical per say; seeing as how the songs are so likable, it is hard to tell a carefully choreographed scene is taking place. I think of this movie as more of a comedy with undertones of romance. You will be reminded of what it was like to be a rebellious teenager; needless to say, you will be reminiscing of your old high school days after seeing this film. Highly recommended for both the family (since it is a very youthful but also for adults since there are many jokes that are funnier with age and experience.'
0 b"Alex D. Linz replaces Macaulay Culkin as the central figure in the third movie in the Home Alone empire. Four industrial spies acquire a missile guidance system computer chip and smuggle it through an airport inside a remote controlled toy car. Because of baggage confusion, grouchy Mrs. Hess (Marian Seldes) gets the car. She gives it to her neighbor, Alex (Linz), just before the spies turn up. The spies rent a house in order to burglarize each house in the neighborhood until they locate the car. Home alone with the chicken pox, Alex calls 911 each time he spots a theft in progress, but the spies always manage to elude the police while Alex is accused of making prank calls. The spies finally turn their attentions toward Alex, unaware that he has rigged devices to cleverly booby-trap his entire house. Home Alone 3 wasn't horrible, but probably shouldn't have been made, you can't just replace Macauley Culkin, Joe Pesci, or Daniel Stern. Home Alone 3 had some funny parts, but I don't like when characters are changed in a movie series, view at own risk."
0 b"There's a good movie lurking here, but this isn't it. The basic idea is good: to explore the moral issues that would face a group of young survivors of the apocalypse. But the logic is so muddled that it's impossible to get involved.<br /><br />For example, our four heroes are (understandably) paranoid about catching the mysterious airborne contagion that's wiped out virtually all of mankind. Yet they wear surgical masks some times, not others. Some times they're fanatical about wiping down with bleach any area touched by an infected person. Other times, they seem completely unconcerned.<br /><br />Worse, after apparently surviving some weeks or months in this new kill-or-be-killed world, these people constantly behave like total newbs. They don't bother accumulating proper equipment, or food. They're forever running out of fuel in the middle of nowhere. They don't take elementary precautions when meeting strangers. And after wading through the rotting corpses of the entire human race, they're as squeamish as sheltered debutantes. You have to constantly wonder how they could have survived this long... and even if they did, why anyone would want to make a movie about them.<br /><br />So when these dweebs stop to agonize over the moral dimensions of their actions, it's impossible to take their soul-searching seriously. Their actions would first have to make some kind of minimal sense.<br /><br />On top of all this, we must contend with the dubious acting abilities of Chris Pine. His portrayal of an arrogant young James T Kirk might have seemed shrewd, when viewed in isolation. But in Carriers he plays on exactly that same note: arrogant and boneheaded. It's impossible not to suspect that this constitutes his entire dramatic range.<br /><br />On the positive side, the film *looks* excellent. It's got an over-sharp, saturated look that really suits the southwestern US locale. But that can't save the truly feeble writing nor the paper-thin (and annoying) characters. Even if you're a fan of the end-of-the-world genre, you should save yourself the agony of watching Carriers."
0 b'I saw this movie at an actual movie theater (probably the \\(2.00 one) with my cousin and uncle. We were around 11 and 12, I guess, and really into scary movies. I remember being so excited to see it because my cool uncle let us pick the movie (and we probably never got to do that again!) and sooo disappointed afterwards!! Just boring and not scary. The only redeeming thing I can remember was Corky Pigeon from Silver Spoons, and that wasn\'t all that great, just someone I recognized. I\'ve seen bad movies before and this one has always stuck out in my mind as the worst. This was from what I can recall, one of the most boring, non-scary, waste of our collective \\)6, and a waste of film. I have read some of the reviews that say it is worth a watch and I say, "Too each his own", but I wouldn\'t even bother. Not even so bad it\'s good.'

Configura il set di dati per le prestazioni

Questi sono due metodi importanti che dovresti usare durante il caricamento dei dati per assicurarti che l'I/O non si blocchi.

.cache() mantiene i dati in memoria dopo che sono stati caricati dal disco. Ciò garantirà che il set di dati non diventi un collo di bottiglia durante l'addestramento del modello. Se il tuo set di dati è troppo grande per essere contenuto nella memoria, puoi anche utilizzare questo metodo per creare una cache su disco performante, che è più efficiente da leggere rispetto a molti file di piccole dimensioni.

.prefetch() si sovrappone alla preelaborazione dei dati e all'esecuzione del modello durante l'addestramento.

Puoi saperne di più su entrambi i metodi e su come memorizzare nella cache i dati su disco nella guida alle prestazioni dei dati .

AUTOTUNE = tf.data.AUTOTUNE

train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

Usando il livello di incorporamento

Keras semplifica l'utilizzo degli incorporamenti di parole. Dai un'occhiata al livello Incorporamento .

Il livello di incorporamento può essere inteso come una tabella di ricerca che esegue il mapping da indici interi (che stanno per parole specifiche) a vettori densi (i loro incorporamenti). La dimensionalità (o larghezza) dell'incorporamento è un parametro che puoi sperimentare per vedere cosa funziona bene per il tuo problema, più o meno allo stesso modo in cui sperimenteresti con il numero di neuroni in uno strato Denso.

# Embed a 1,000 word vocabulary into 5 dimensions.
embedding_layer = tf.keras.layers.Embedding(1000, 5)

Quando crei un livello di incorporamento, i pesi per l'incorporamento vengono inizializzati in modo casuale (proprio come qualsiasi altro livello). Durante l'allenamento, vengono gradualmente regolati tramite backpropagation. Una volta addestrati, gli incorporamenti di parole appresi codificheranno approssimativamente le somiglianze tra le parole (come sono state apprese per il problema specifico su cui è stato addestrato il modello).

Se si passa un intero a un livello di incorporamento, il risultato sostituisce ogni intero con il vettore dalla tabella di incorporamento:

result = embedding_layer(tf.constant([1, 2, 3]))
result.numpy()
array([[ 0.01318491, -0.02219239,  0.024673  , -0.03208025,  0.02297195],
       [-0.00726584,  0.03731754, -0.01209557, -0.03887399, -0.02407478],
       [ 0.04477594,  0.04504738, -0.02220147, -0.03642888, -0.04688282]],
      dtype=float32)

Per problemi di testo o sequenza, il livello Incorporamento prende un tensore 2D di numeri interi, di forma (samples, sequence_length) , dove ogni voce è una sequenza di numeri interi. Può incorporare sequenze di lunghezza variabile. È possibile inserire nel livello di incorporamento sopra i lotti con forme (32, 10) (lotto di 32 sequenze di lunghezza 10) o (64, 15) (lotto di 64 sequenze di lunghezza 15).

Il tensore restituito ha un asse in più rispetto all'input, i vettori di incorporamento sono allineati lungo il nuovo ultimo asse. Passagli un batch di input (2, 3) e l'output è (2, 3, N)

result = embedding_layer(tf.constant([[0, 1, 2], [3, 4, 5]]))
result.shape
TensorShape([2, 3, 5])

Quando viene fornito un batch di sequenze come input, un livello di incorporamento restituisce un tensore a virgola mobile 3D, di forma (samples, sequence_length, embedding_dimensionality) . Per convertire da questa sequenza di lunghezza variabile a una rappresentazione fissa ci sono una varietà di approcci standard. È possibile utilizzare un livello RNN, Attenzione o pool prima di passarlo a un livello Dense. Questo tutorial utilizza il pooling perché è il più semplice. La classificazione del testo con un tutorial RNN è un buon passo successivo.

Preelaborazione del testo

Quindi, definisci i passaggi di preelaborazione del set di dati richiesti per il tuo modello di classificazione del sentiment. Inizializza un livello TextVectorization con i parametri desiderati per vettorializzare le recensioni dei film. Puoi saperne di più sull'uso di questo livello nel tutorial sulla classificazione del testo .

# Create a custom standardization function to strip HTML break tags '<br />'.
def custom_standardization(input_data):
  lowercase = tf.strings.lower(input_data)
  stripped_html = tf.strings.regex_replace(lowercase, '<br />', ' ')
  return tf.strings.regex_replace(stripped_html,
                                  '[%s]' % re.escape(string.punctuation), '')


# Vocabulary size and number of words in a sequence.
vocab_size = 10000
sequence_length = 100

# Use the text vectorization layer to normalize, split, and map strings to
# integers. Note that the layer uses the custom standardization defined above.
# Set maximum_sequence length as all samples are not of the same length.
vectorize_layer = TextVectorization(
    standardize=custom_standardization,
    max_tokens=vocab_size,
    output_mode='int',
    output_sequence_length=sequence_length)

# Make a text-only dataset (no labels) and call adapt to build the vocabulary.
text_ds = train_ds.map(lambda x, y: x)
vectorize_layer.adapt(text_ds)

Creare un modello di classificazione

Utilizza l' API Keras Sequential per definire il modello di classificazione del sentiment. In questo caso si tratta di un modello in stile "borsa continua di parole".

  • Il livello TextVectorization trasforma le stringhe in indici di vocabolario. Hai già inizializzato vectorize_layer come livello TextVectorization e costruito il suo vocabolario chiamando adapt su text_ds . Ora vectorize_layer può essere utilizzato come primo livello del tuo modello di classificazione end-to-end, alimentando le stringhe trasformate nel livello di incorporamento.
  • Il livello di Embedding prende il vocabolario con codifica intera e cerca il vettore di incorporamento per ogni indice di parola. Questi vettori vengono appresi come treni modello. I vettori aggiungono una dimensione all'array di output. Le dimensioni risultanti sono: (batch, sequence, embedding) .

  • Il livello GlobalAveragePooling1D restituisce un vettore di output a lunghezza fissa per ogni esempio calcolando la media sulla dimensione della sequenza. Ciò consente al modello di gestire input di lunghezza variabile, nel modo più semplice possibile.

  • Il vettore di output a lunghezza fissa viene convogliato attraverso uno strato completamente connesso ( Dense ) con 16 unità nascoste.

  • L'ultimo strato è densamente connesso con un singolo nodo di output.

embedding_dim=16

model = Sequential([
  vectorize_layer,
  Embedding(vocab_size, embedding_dim, name="embedding"),
  GlobalAveragePooling1D(),
  Dense(16, activation='relu'),
  Dense(1)
])

Compila e addestra il modello

Utilizzerai TensorBoard per visualizzare le metriche, inclusa la perdita e l'accuratezza. Creare un tf.keras.callbacks.TensorBoard .

tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir="logs")

Compilare e addestrare il modello utilizzando l'ottimizzatore Adam e la perdita BinaryCrossentropy .

model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])
model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=15,
    callbacks=[tensorboard_callback])
Epoch 1/15
20/20 [==============================] - 2s 71ms/step - loss: 0.6910 - accuracy: 0.5028 - val_loss: 0.6878 - val_accuracy: 0.4886
Epoch 2/15
20/20 [==============================] - 1s 57ms/step - loss: 0.6838 - accuracy: 0.5028 - val_loss: 0.6791 - val_accuracy: 0.4886
Epoch 3/15
20/20 [==============================] - 1s 58ms/step - loss: 0.6726 - accuracy: 0.5028 - val_loss: 0.6661 - val_accuracy: 0.4886
Epoch 4/15
20/20 [==============================] - 1s 58ms/step - loss: 0.6563 - accuracy: 0.5028 - val_loss: 0.6481 - val_accuracy: 0.4886
Epoch 5/15
20/20 [==============================] - 1s 58ms/step - loss: 0.6343 - accuracy: 0.5061 - val_loss: 0.6251 - val_accuracy: 0.5066
Epoch 6/15
20/20 [==============================] - 1s 58ms/step - loss: 0.6068 - accuracy: 0.5634 - val_loss: 0.5982 - val_accuracy: 0.5762
Epoch 7/15
20/20 [==============================] - 1s 58ms/step - loss: 0.5752 - accuracy: 0.6405 - val_loss: 0.5690 - val_accuracy: 0.6386
Epoch 8/15
20/20 [==============================] - 1s 58ms/step - loss: 0.5412 - accuracy: 0.7036 - val_loss: 0.5390 - val_accuracy: 0.6850
Epoch 9/15
20/20 [==============================] - 1s 59ms/step - loss: 0.5064 - accuracy: 0.7479 - val_loss: 0.5106 - val_accuracy: 0.7222
Epoch 10/15
20/20 [==============================] - 1s 59ms/step - loss: 0.4734 - accuracy: 0.7774 - val_loss: 0.4855 - val_accuracy: 0.7430
Epoch 11/15
20/20 [==============================] - 1s 59ms/step - loss: 0.4432 - accuracy: 0.7971 - val_loss: 0.4636 - val_accuracy: 0.7570
Epoch 12/15
20/20 [==============================] - 1s 58ms/step - loss: 0.4161 - accuracy: 0.8155 - val_loss: 0.4453 - val_accuracy: 0.7674
Epoch 13/15
20/20 [==============================] - 1s 59ms/step - loss: 0.3921 - accuracy: 0.8304 - val_loss: 0.4303 - val_accuracy: 0.7780
Epoch 14/15
20/20 [==============================] - 1s 61ms/step - loss: 0.3711 - accuracy: 0.8398 - val_loss: 0.4181 - val_accuracy: 0.7884
Epoch 15/15
20/20 [==============================] - 1s 58ms/step - loss: 0.3524 - accuracy: 0.8493 - val_loss: 0.4082 - val_accuracy: 0.7948
<keras.callbacks.History at 0x7fca579745d0>

Con questo approccio il modello raggiunge un'accuratezza di convalida di circa il 78% (si noti che il modello è overfitting poiché l'accuratezza dell'allenamento è maggiore).

Puoi esaminare il riepilogo del modello per saperne di più su ogni livello del modello.

model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 text_vectorization (TextVec  (None, 100)              0         
 torization)                                                     
                                                                 
 embedding (Embedding)       (None, 100, 16)           160000    
                                                                 
 global_average_pooling1d (G  (None, 16)               0         
 lobalAveragePooling1D)                                          
                                                                 
 dense (Dense)               (None, 16)                272       
                                                                 
 dense_1 (Dense)             (None, 1)                 17        
                                                                 
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________

Visualizza le metriche del modello in TensorBoard.

#docs_infra: no_execute
%load_ext tensorboard
%tensorboard --logdir logs

embeddings_classifier_accuracy.png

Recupera gli incorporamenti di parole addestrati e salvali su disco

Successivamente, recupera le incorporazioni di parole apprese durante l'allenamento. Gli incorporamenti sono pesi del livello Incorporamento nel modello. La matrice dei pesi è di forma (vocab_size, embedding_dimension) .

Ottieni i pesi dal modello usando get_layer() e get_weights() . La funzione get_vocabulary() fornisce il vocabolario per creare un file di metadati con un token per riga.

weights = model.get_layer('embedding').get_weights()[0]
vocab = vectorize_layer.get_vocabulary()

Scrivi i pesi su disco. Per utilizzare Embedding Projector , caricherai due file in formato separato da tabulazioni: un file di vettori (contenente l'incorporamento) e un file di metadati (contenente le parole).

out_v = io.open('vectors.tsv', 'w', encoding='utf-8')
out_m = io.open('metadata.tsv', 'w', encoding='utf-8')

for index, word in enumerate(vocab):
  if index == 0:
    continue  # skip 0, it's padding.
  vec = weights[index]
  out_v.write('\t'.join([str(x) for x in vec]) + "\n")
  out_m.write(word + "\n")
out_v.close()
out_m.close()

Se stai eseguendo questo tutorial in Colaboratory , puoi utilizzare il seguente snippet per scaricare questi file sul tuo computer locale (o utilizzare il browser di file, Visualizza -> Sommario -> Browser di file ).

try:
  from google.colab import files
  files.download('vectors.tsv')
  files.download('metadata.tsv')
except Exception:
  pass

Visualizza gli incorporamenti

Per visualizzare gli incorporamenti, caricarli sul proiettore per incorporamenti.

Apri il proiettore di incorporamento (questo può anche essere eseguito in un'istanza TensorBoard locale).

  • Fare clic su "Carica dati".

  • Carica i due file che hai creato sopra: vecs.tsv e meta.tsv .

Verranno ora visualizzati gli incorporamenti che hai addestrato. Puoi cercare parole per trovare i loro vicini più vicini. Ad esempio, prova a cercare "bello". Potresti vedere i vicini come "meravigliosi".

Prossimi passi

Questo tutorial ti ha mostrato come addestrare e visualizzare incorporamenti di parole da zero su un piccolo set di dati.