palabra2vec

Ver en TensorFlow.org Ejecutar en Google Colab Ver fuente en GitHub Descargar libreta

word2vec no es un algoritmo singular, sino una familia de arquitecturas modelo y optimizaciones que se pueden usar para aprender incrustaciones de palabras a partir de grandes conjuntos de datos. Las incrustaciones aprendidas a través de word2vec han demostrado ser exitosas en una variedad de tareas de procesamiento de lenguaje natural posteriores.

Estos artículos propusieron dos métodos para aprender las representaciones de las palabras:

  • Modelo continuo de bolsa de palabras : predice la palabra del medio en función de las palabras del contexto circundante. El contexto consta de unas pocas palabras antes y después de la palabra actual (en el medio). Esta arquitectura se denomina modelo de bolsa de palabras, ya que el orden de las palabras en el contexto no es importante.
  • Modelo de salto de gramo continuo : predice palabras dentro de un cierto rango antes y después de la palabra actual en la misma oración. Un ejemplo trabajado de esto se da a continuación.

En este tutorial, usará el método skip-gram. Primero, explorará skip-grams y otros conceptos usando una sola oración como ilustración. A continuación, entrenará su propio modelo word2vec en un pequeño conjunto de datos. Este tutorial también contiene código para exportar las incrustaciones entrenadas y visualizarlas en TensorFlow Embedding Projector .

Skip-gram y muestreo negativo

Mientras que un modelo de bolsa de palabras predice una palabra dado el contexto vecino, un modelo skip-gram predice el contexto (o vecinos) de una palabra, dada la palabra misma. El modelo está entrenado en skip-grams, que son n-grams que permiten omitir tokens (vea el diagrama a continuación para ver un ejemplo). El contexto de una palabra se puede representar a través de un conjunto de pares de omisión de gramo (target_word, context_word) donde context_word aparece en el contexto vecino de target_word .

Considere la siguiente oración de ocho palabras:

El ancho camino brillaba bajo el sol abrasador.

Las palabras de contexto para cada una de las 8 palabras de esta oración están definidas por un tamaño de ventana. El tamaño de la ventana determina el intervalo de palabras a ambos lados de target_word que puede considerarse una context word . A continuación se muestra una tabla de saltos de gramo para las palabras de destino en función de diferentes tamaños de ventana.

word2vec_skipgramas

El objetivo de entrenamiento del modelo skip-gram es maximizar la probabilidad de predecir palabras de contexto dada la palabra objetivo. Para una secuencia de palabras w 1 , w 2 , ... w T , el objetivo se puede escribir como la probabilidad logarítmica promedio

word2vec_skipgram_objetivo

donde c es el tamaño del contexto de entrenamiento. La formulación básica de skip-gram define esta probabilidad usando la función softmax.

word2vec_full_softmax

donde v y v ' son representaciones de vector objetivo y de contexto de palabras y W es el tamaño del vocabulario.

Calcular el denominador de esta formulación implica realizar un softmax completo sobre todas las palabras del vocabulario, que a menudo son términos grandes (10 5 -10 7 ).

La función de pérdida de estimación contrastiva de ruido (NCE) es una aproximación eficiente para un softmax completo. Con el objetivo de aprender incrustaciones de palabras en lugar de modelar la distribución de palabras, la pérdida de NCE se puede simplificar para usar un muestreo negativo.

El objetivo de muestreo negativo simplificado para una palabra objetivo es distinguir la palabra de contexto de num_ns muestras negativas extraídas de la distribución de ruido Pn (w) de palabras. Más precisamente, una aproximación eficiente de softmax completo sobre el vocabulario es, para un par de saltos de gramo, plantear la pérdida de una palabra objetivo como un problema de clasificación entre la palabra de contexto y num_ns muestras negativas.

Una muestra negativa se define como un (target_word, context_word) tal que context_word no aparece en la vecindad window_size de target_word . Para la oración de ejemplo, estas son algunas muestras negativas potenciales (cuando window_size es 2 ).

(hot, shimmered)
(wide, hot)
(wide, sun)

En la siguiente sección, generará skip-grams y muestras negativas para una sola oración. También aprenderá sobre técnicas de submuestreo y entrenará un modelo de clasificación para ejemplos de entrenamiento positivos y negativos más adelante en el tutorial.

Configuración

import io
import re
import string
import tqdm

import numpy as np

import tensorflow as tf
from tensorflow.keras import layers
# Load the TensorBoard notebook extension
%load_ext tensorboard
SEED = 42
AUTOTUNE = tf.data.AUTOTUNE

Vectorizar una oración de ejemplo

Considere la siguiente oración:

El ancho camino brillaba bajo el sol abrasador.

Tokenizar la oración:

sentence = "The wide road shimmered in the hot sun"
tokens = list(sentence.lower().split())
print(len(tokens))
8

Cree un vocabulario para guardar asignaciones de tokens a índices enteros:

vocab, index = {}, 1  # start indexing from 1
vocab['<pad>'] = 0  # add a padding token
for token in tokens:
  if token not in vocab:
    vocab[token] = index
    index += 1
vocab_size = len(vocab)
print(vocab)
{'<pad>': 0, 'the': 1, 'wide': 2, 'road': 3, 'shimmered': 4, 'in': 5, 'hot': 6, 'sun': 7}

Cree un vocabulario inverso para guardar asignaciones de índices enteros a tokens:

inverse_vocab = {index: token for token, index in vocab.items()}
print(inverse_vocab)
{0: '<pad>', 1: 'the', 2: 'wide', 3: 'road', 4: 'shimmered', 5: 'in', 6: 'hot', 7: 'sun'}

Vectoriza tu oración:

example_sequence = [vocab[word] for word in tokens]
print(example_sequence)
[1, 2, 3, 4, 5, 1, 6, 7]

Genera saltos de gramo a partir de una oración

El módulo tf.keras.preprocessing.sequence proporciona funciones útiles que simplifican la preparación de datos para word2vec. Puede usar tf.keras.preprocessing.sequence.skipgrams para generar pares de saltos de gramo a partir de example_sequence con un window_size dado a partir de tokens en el rango [0, vocab_size) .

window_size = 2
positive_skip_grams, _ = tf.keras.preprocessing.sequence.skipgrams(
      example_sequence,
      vocabulary_size=vocab_size,
      window_size=window_size,
      negative_samples=0)
print(len(positive_skip_grams))
26

Imprime algunos skip-grams positivos:

for target, context in positive_skip_grams[:5]:
  print(f"({target}, {context}): ({inverse_vocab[target]}, {inverse_vocab[context]})")
(2, 3): (wide, road)
(5, 3): (in, road)
(4, 2): (shimmered, wide)
(1, 7): (the, sun)
(4, 1): (shimmered, the)

Muestreo negativo para un skip-gram

La función skipgrams devuelve todos los pares skip-gram positivos deslizándose sobre un intervalo de ventana determinado. Para producir pares de saltos de gramo adicionales que sirvan como muestras negativas para el entrenamiento, debe muestrear palabras aleatorias del vocabulario. Utilice la función tf.random.log_uniform_candidate_sampler para muestrear num_ns de muestras negativas para una palabra de destino determinada en una ventana. Puede llamar a la función en la palabra de destino de un skip-grams y pasar la palabra de contexto como clase verdadera para excluirla de la muestra.

# Get target and context words for one positive skip-gram.
target_word, context_word = positive_skip_grams[0]

# Set the number of negative samples per positive context.
num_ns = 4

context_class = tf.reshape(tf.constant(context_word, dtype="int64"), (1, 1))
negative_sampling_candidates, _, _ = tf.random.log_uniform_candidate_sampler(
    true_classes=context_class,  # class that should be sampled as 'positive'
    num_true=1,  # each positive skip-gram has 1 positive context class
    num_sampled=num_ns,  # number of negative context words to sample
    unique=True,  # all the negative samples should be unique
    range_max=vocab_size,  # pick index of the samples from [0, vocab_size]
    seed=SEED,  # seed for reproducibility
    name="negative_sampling"  # name of this operation
)
print(negative_sampling_candidates)
print([inverse_vocab[index.numpy()] for index in negative_sampling_candidates])
tf.Tensor([2 1 4 3], shape=(4,), dtype=int64)
['wide', 'the', 'shimmered', 'road']

Construya un ejemplo de entrenamiento

Para un skip-gram positivo dado (target_word, context_word) , ahora también tiene num_ns palabras de contexto muestreadas negativas que no aparecen en la vecindad del tamaño de la ventana de target_word . Reúna 1 context_word positiva y num_ns palabras de contexto negativas en un tensor. Esto produce un conjunto de skip-grams positivos (etiquetados como 1 ) y muestras negativas (etiquetadas como 0 ) para cada palabra objetivo.

# Add a dimension so you can use concatenation (in the next step).
negative_sampling_candidates = tf.expand_dims(negative_sampling_candidates, 1)

# Concatenate a positive context word with negative sampled words.
context = tf.concat([context_class, negative_sampling_candidates], 0)

# Label the first context word as `1` (positive) followed by `num_ns` `0`s (negative).
label = tf.constant([1] + [0]*num_ns, dtype="int64")

# Reshape the target to shape `(1,)` and context and label to `(num_ns+1,)`.
target = tf.squeeze(target_word)
context = tf.squeeze(context)
label = tf.squeeze(label)

Consulte el contexto y las etiquetas correspondientes para la palabra de destino del ejemplo de salto de gramo anterior:

print(f"target_index    : {target}")
print(f"target_word     : {inverse_vocab[target_word]}")
print(f"context_indices : {context}")
print(f"context_words   : {[inverse_vocab[c.numpy()] for c in context]}")
print(f"label           : {label}")
target_index    : 2
target_word     : wide
context_indices : [3 2 1 4 3]
context_words   : ['road', 'wide', 'the', 'shimmered', 'road']
label           : [1 0 0 0 0]

Una tupla de tensores (target, context, label) constituye un ejemplo de entrenamiento para entrenar su modelo word2vec de muestreo negativo skip-gram. Observe que el destino tiene forma (1,) mientras que el contexto y la etiqueta tienen forma (1+num_ns,)

print("target  :", target)
print("context :", context)
print("label   :", label)
target  : tf.Tensor(2, shape=(), dtype=int32)
context : tf.Tensor([3 2 1 4 3], shape=(5,), dtype=int64)
label   : tf.Tensor([1 0 0 0 0], shape=(5,), dtype=int64)

Resumen

Este diagrama resume el procedimiento para generar un ejemplo de entrenamiento a partir de una oración:

word2vec_negative_sampling

Observe que las palabras temperature y code no forman parte de la oración de entrada. Pertenecen al vocabulario como algunos otros índices utilizados en el diagrama anterior.

Compilar todos los pasos en una función

Tabla de muestreo de salto de gramo

Un conjunto de datos grande significa un vocabulario más grande con una mayor cantidad de palabras más frecuentes, como palabras vacías. Los ejemplos de entrenamiento obtenidos a partir de muestras de palabras comunes ( the , is , on ) no agregan mucha información útil para que el modelo aprenda. Mikolov et al. sugiera el submuestreo de palabras frecuentes como una práctica útil para mejorar la calidad de incrustación.

La función tf.keras.preprocessing.sequence.skipgrams acepta un argumento de tabla de muestreo para codificar las probabilidades de muestrear cualquier token. Puede usar tf.keras.preprocessing.sequence.make_sampling_table para generar una tabla de muestreo probabilístico basada en el rango de frecuencia de palabras y pasarla a la función skipgrams . Inspeccione las probabilidades de muestreo para un vocab_size de 10.

sampling_table = tf.keras.preprocessing.sequence.make_sampling_table(size=10)
print(sampling_table)
[0.00315225 0.00315225 0.00547597 0.00741556 0.00912817 0.01068435
 0.01212381 0.01347162 0.01474487 0.0159558 ]

sampling_table[i] denota la probabilidad de muestrear la i-ésima palabra más común en un conjunto de datos. La función asume una distribución de Zipf de las frecuencias de palabras para el muestreo.

Generar datos de entrenamiento

Compile todos los pasos descritos anteriormente en una función que se pueda llamar en una lista de oraciones vectorizadas obtenidas de cualquier conjunto de datos de texto. Tenga en cuenta que la tabla de muestreo se construye antes de muestrear pares de palabras de salto de gramo. Utilizará esta función en las secciones posteriores.

# Generates skip-gram pairs with negative sampling for a list of sequences
# (int-encoded sentences) based on window size, number of negative samples
# and vocabulary size.
def generate_training_data(sequences, window_size, num_ns, vocab_size, seed):
  # Elements of each training example are appended to these lists.
  targets, contexts, labels = [], [], []

  # Build the sampling table for `vocab_size` tokens.
  sampling_table = tf.keras.preprocessing.sequence.make_sampling_table(vocab_size)

  # Iterate over all sequences (sentences) in the dataset.
  for sequence in tqdm.tqdm(sequences):

    # Generate positive skip-gram pairs for a sequence (sentence).
    positive_skip_grams, _ = tf.keras.preprocessing.sequence.skipgrams(
          sequence,
          vocabulary_size=vocab_size,
          sampling_table=sampling_table,
          window_size=window_size,
          negative_samples=0)

    # Iterate over each positive skip-gram pair to produce training examples
    # with a positive context word and negative samples.
    for target_word, context_word in positive_skip_grams:
      context_class = tf.expand_dims(
          tf.constant([context_word], dtype="int64"), 1)
      negative_sampling_candidates, _, _ = tf.random.log_uniform_candidate_sampler(
          true_classes=context_class,
          num_true=1,
          num_sampled=num_ns,
          unique=True,
          range_max=vocab_size,
          seed=SEED,
          name="negative_sampling")

      # Build context and label vectors (for one target word)
      negative_sampling_candidates = tf.expand_dims(
          negative_sampling_candidates, 1)

      context = tf.concat([context_class, negative_sampling_candidates], 0)
      label = tf.constant([1] + [0]*num_ns, dtype="int64")

      # Append each element from the training example to global lists.
      targets.append(target_word)
      contexts.append(context)
      labels.append(label)

  return targets, contexts, labels

Preparar datos de entrenamiento para word2vec

Con una comprensión de cómo trabajar con una oración para un modelo word2vec basado en muestreo negativo skip-gram, ¡puede proceder a generar ejemplos de entrenamiento a partir de una lista más grande de oraciones!

Descargar corpus de texto

Utilizará un archivo de texto de la escritura de Shakespeare para este tutorial. Cambie la siguiente línea para ejecutar este código en sus propios datos.

path_to_file = tf.keras.utils.get_file('shakespeare.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt
1122304/1115394 [==============================] - 0s 0us/step
1130496/1115394 [==============================] - 0s 0us/step

Lea el texto del archivo e imprima las primeras líneas:

with open(path_to_file) as f:
  lines = f.read().splitlines()
for line in lines[:20]:
  print(line)
First Citizen:
Before we proceed any further, hear me speak.

All:
Speak, speak.

First Citizen:
You are all resolved rather to die than to famish?

All:
Resolved. resolved.

First Citizen:
First, you know Caius Marcius is chief enemy to the people.

All:
We know't, we know't.

First Citizen:
Let us kill him, and we'll have corn at our own price.

Use las líneas no vacías para construir un objeto tf.data.TextLineDataset para los siguientes pasos:

text_ds = tf.data.TextLineDataset(path_to_file).filter(lambda x: tf.cast(tf.strings.length(x), bool))

Vectorizar oraciones del corpus

Puede usar la capa TextVectorization para vectorizar oraciones del corpus. Obtenga más información sobre el uso de esta capa en este tutorial de clasificación de texto . Observe en las primeras oraciones anteriores que el texto debe estar en un caso y se debe eliminar la puntuación. Para hacer esto, defina una custom_standardization function que se pueda usar en la capa TextVectorization.

# Now, create a custom standardization function to lowercase the text and
# remove punctuation.
def custom_standardization(input_data):
  lowercase = tf.strings.lower(input_data)
  return tf.strings.regex_replace(lowercase,
                                  '[%s]' % re.escape(string.punctuation), '')


# Define the vocabulary size and the number of words in a sequence.
vocab_size = 4096
sequence_length = 10

# Use the `TextVectorization` layer to normalize, split, and map strings to
# integers. Set the `output_sequence_length` length to pad all samples to the
# same length.
vectorize_layer = layers.TextVectorization(
    standardize=custom_standardization,
    max_tokens=vocab_size,
    output_mode='int',
    output_sequence_length=sequence_length)

Llame a TextVectorization.adapt en el conjunto de datos de texto para crear vocabulario.

vectorize_layer.adapt(text_ds.batch(1024))

Una vez que el estado de la capa se ha adaptado para representar el corpus de texto, se puede acceder al vocabulario con TextVectorization.get_vocabulary . Esta función devuelve una lista de todos los tokens de vocabulario ordenados (descendentemente) por su frecuencia.

# Save the created vocabulary for reference.
inverse_vocab = vectorize_layer.get_vocabulary()
print(inverse_vocab[:20])
['', '[UNK]', 'the', 'and', 'to', 'i', 'of', 'you', 'my', 'a', 'that', 'in', 'is', 'not', 'for', 'with', 'me', 'it', 'be', 'your']

vectorize_layer ahora se puede usar para generar vectores para cada elemento en text_ds (un tf.data.Dataset ). Aplique Dataset.batch , Dataset.prefetch , Dataset.map y Dataset.unbatch .

# Vectorize the data in text_ds.
text_vector_ds = text_ds.batch(1024).prefetch(AUTOTUNE).map(vectorize_layer).unbatch()

Obtener secuencias del conjunto de datos

Ahora tiene un tf.data.Dataset de oraciones codificadas con enteros. Para preparar el conjunto de datos para entrenar un modelo word2vec, aplane el conjunto de datos en una lista de secuencias de vectores de oraciones. Este paso es necesario, ya que iteraría sobre cada oración en el conjunto de datos para producir ejemplos positivos y negativos.

sequences = list(text_vector_ds.as_numpy_iterator())
print(len(sequences))
32777

Inspeccione algunos ejemplos de sequences :

for seq in sequences[:5]:
  print(f"{seq} => {[inverse_vocab[i] for i in seq]}")
[ 89 270   0   0   0   0   0   0   0   0] => ['first', 'citizen', '', '', '', '', '', '', '', '']
[138  36 982 144 673 125  16 106   0   0] => ['before', 'we', 'proceed', 'any', 'further', 'hear', 'me', 'speak', '', '']
[34  0  0  0  0  0  0  0  0  0] => ['all', '', '', '', '', '', '', '', '', '']
[106 106   0   0   0   0   0   0   0   0] => ['speak', 'speak', '', '', '', '', '', '', '', '']
[ 89 270   0   0   0   0   0   0   0   0] => ['first', 'citizen', '', '', '', '', '', '', '', '']

Genera ejemplos de entrenamiento a partir de secuencias.

sequences ahora es una lista de oraciones codificadas int. Simplemente llame a la función generate_training_data definida anteriormente para generar ejemplos de entrenamiento para el modelo word2vec. En resumen, la función itera sobre cada palabra de cada secuencia para recopilar palabras de contexto positivas y negativas. La longitud del objetivo, los contextos y las etiquetas deben ser los mismos y representar el número total de ejemplos de capacitación.

targets, contexts, labels = generate_training_data(
    sequences=sequences,
    window_size=2,
    num_ns=4,
    vocab_size=vocab_size,
    seed=SEED)

targets = np.array(targets)
contexts = np.array(contexts)[:,:,0]
labels = np.array(labels)

print('\n')
print(f"targets.shape: {targets.shape}")
print(f"contexts.shape: {contexts.shape}")
print(f"labels.shape: {labels.shape}")
100%|██████████| 32777/32777 [00:40<00:00, 811.35it/s]
targets.shape: (66005,)
contexts.shape: (66005, 5)
labels.shape: (66005, 5)

Configurar el conjunto de datos para el rendimiento

Para realizar un procesamiento por lotes eficiente para la cantidad potencialmente grande de ejemplos de capacitación, use la API tf.data.Dataset . Después de este paso, tendría un objeto tf.data.Dataset de (target_word, context_word), (label) elementos para entrenar su modelo word2vec.

BATCH_SIZE = 1024
BUFFER_SIZE = 10000
dataset = tf.data.Dataset.from_tensor_slices(((targets, contexts), labels))
dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
print(dataset)
<BatchDataset element_spec=((TensorSpec(shape=(1024,), dtype=tf.int64, name=None), TensorSpec(shape=(1024, 5), dtype=tf.int64, name=None)), TensorSpec(shape=(1024, 5), dtype=tf.int64, name=None))>

Aplique Dataset.cache y Dataset.prefetch para mejorar el rendimiento:

dataset = dataset.cache().prefetch(buffer_size=AUTOTUNE)
print(dataset)
<PrefetchDataset element_spec=((TensorSpec(shape=(1024,), dtype=tf.int64, name=None), TensorSpec(shape=(1024, 5), dtype=tf.int64, name=None)), TensorSpec(shape=(1024, 5), dtype=tf.int64, name=None))>

modelo y entrenamiento

El modelo word2vec se puede implementar como un clasificador para distinguir entre palabras de contexto verdadero de skip-grams y palabras de contexto falso obtenidas mediante muestreo negativo. Puede realizar una multiplicación de producto punto entre las incrustaciones de palabras objetivo y de contexto para obtener predicciones para etiquetas y calcular la función de pérdida contra etiquetas verdaderas en el conjunto de datos.

Modelo word2vec subclasificado

Utilice la API de subclases de Keras para definir su modelo word2vec con las siguientes capas:

  • target_embedding : una capa tf.keras.layers.Embedding , que busca la incrustación de una palabra cuando aparece como palabra objetivo. El número de parámetros en esta capa es (vocab_size * embedding_dim) .
  • context_embedding : otra capa tf.keras.layers.Embedding , que busca la incrustación de una palabra cuando aparece como una palabra de contexto. El número de parámetros en esta capa es el mismo que el de target_embedding , es decir (vocab_size * embedding_dim) .
  • dots : una capa tf.keras.layers.Dot que calcula el producto escalar de las incrustaciones de destino y contexto de un par de entrenamiento.
  • flatten : Una capa tf.keras.layers.Flatten para aplanar los resultados de la capa de dots en logits.

Con el modelo subclasificado, puede definir la función call() que acepta pares (target, context) que luego se pueden pasar a su capa de incrustación correspondiente. context_embedding para realizar un producto de punto con target_embedding y devuelva el resultado aplanado.

class Word2Vec(tf.keras.Model):
  def __init__(self, vocab_size, embedding_dim):
    super(Word2Vec, self).__init__()
    self.target_embedding = layers.Embedding(vocab_size,
                                      embedding_dim,
                                      input_length=1,
                                      name="w2v_embedding")
    self.context_embedding = layers.Embedding(vocab_size,
                                       embedding_dim,
                                       input_length=num_ns+1)

  def call(self, pair):
    target, context = pair
    # target: (batch, dummy?)  # The dummy axis doesn't exist in TF2.7+
    # context: (batch, context)
    if len(target.shape) == 2:
      target = tf.squeeze(target, axis=1)
    # target: (batch,)
    word_emb = self.target_embedding(target)
    # word_emb: (batch, embed)
    context_emb = self.context_embedding(context)
    # context_emb: (batch, context, embed)
    dots = tf.einsum('be,bce->bc', word_emb, context_emb)
    # dots: (batch, context)
    return dots

Defina la función de pérdida y compile el modelo

Para simplificar, puede utilizar tf.keras.losses.CategoricalCrossEntropy como alternativa a la pérdida de muestreo negativa. Si desea escribir su propia función de pérdida personalizada, también puede hacerlo de la siguiente manera:

def custom_loss(x_logit, y_true):
      return tf.nn.sigmoid_cross_entropy_with_logits(logits=x_logit, labels=y_true)

¡Es hora de construir tu modelo! Cree una instancia de su clase word2vec con una dimensión de incrustación de 128 (podría experimentar con diferentes valores). Compile el modelo con el optimizador tf.keras.optimizers.Adam .

embedding_dim = 128
word2vec = Word2Vec(vocab_size, embedding_dim)
word2vec.compile(optimizer='adam',
                 loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),
                 metrics=['accuracy'])

También define una devolución de llamada para registrar estadísticas de entrenamiento para Tensorboard:

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

Entrene el modelo en el conjunto de dataset para una cierta cantidad de épocas:

word2vec.fit(dataset, epochs=20, callbacks=[tensorboard_callback])
Epoch 1/20
64/64 [==============================] - 1s 4ms/step - loss: 1.6081 - accuracy: 0.2343
Epoch 2/20
64/64 [==============================] - 0s 3ms/step - loss: 1.5878 - accuracy: 0.5475
Epoch 3/20
64/64 [==============================] - 0s 3ms/step - loss: 1.5381 - accuracy: 0.5813
Epoch 4/20
64/64 [==============================] - 0s 3ms/step - loss: 1.4545 - accuracy: 0.5620
Epoch 5/20
64/64 [==============================] - 0s 3ms/step - loss: 1.3567 - accuracy: 0.5764
Epoch 6/20
64/64 [==============================] - 0s 3ms/step - loss: 1.2603 - accuracy: 0.6070
Epoch 7/20
64/64 [==============================] - 0s 3ms/step - loss: 1.1703 - accuracy: 0.6403
Epoch 8/20
64/64 [==============================] - 0s 3ms/step - loss: 1.0866 - accuracy: 0.6756
Epoch 9/20
64/64 [==============================] - 0s 3ms/step - loss: 1.0090 - accuracy: 0.7087
Epoch 10/20
64/64 [==============================] - 0s 3ms/step - loss: 0.9368 - accuracy: 0.7371
Epoch 11/20
64/64 [==============================] - 0s 3ms/step - loss: 0.8699 - accuracy: 0.7631
Epoch 12/20
64/64 [==============================] - 0s 3ms/step - loss: 0.8081 - accuracy: 0.7849
Epoch 13/20
64/64 [==============================] - 0s 3ms/step - loss: 0.7512 - accuracy: 0.8052
Epoch 14/20
64/64 [==============================] - 0s 3ms/step - loss: 0.6991 - accuracy: 0.8230
Epoch 15/20
64/64 [==============================] - 0s 3ms/step - loss: 0.6514 - accuracy: 0.8378
Epoch 16/20
64/64 [==============================] - 0s 3ms/step - loss: 0.6079 - accuracy: 0.8517
Epoch 17/20
64/64 [==============================] - 0s 3ms/step - loss: 0.5683 - accuracy: 0.8641
Epoch 18/20
64/64 [==============================] - 0s 3ms/step - loss: 0.5322 - accuracy: 0.8747
Epoch 19/20
64/64 [==============================] - 0s 3ms/step - loss: 0.4994 - accuracy: 0.8844
Epoch 20/20
64/64 [==============================] - 0s 3ms/step - loss: 0.4695 - accuracy: 0.8935
<keras.callbacks.History at 0x7fc21c237050>

Tensorboard ahora muestra la precisión y la pérdida del modelo word2vec:

#docs_infra: no_execute
%tensorboard --logdir logs

Incorporación de búsqueda y análisis

Obtenga los pesos del modelo usando Model.get_layer y Layer.get_weights . La función TextVectorization.get_vocabulary proporciona el vocabulario para crear un archivo de metadatos con un token por línea.

weights = word2vec.get_layer('w2v_embedding').get_weights()[0]
vocab = vectorize_layer.get_vocabulary()

Cree y guarde los vectores y los archivos de metadatos:

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()

Descarga los vectors.tsv y metadata.tsv para analizar las incrustaciones obtenidas en el Embedding Projector :

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

Próximos pasos

Este tutorial le ha mostrado cómo implementar un modelo word2vec skip-gram con muestreo negativo desde cero y visualizar las incrustaciones de palabras obtenidas.