Ajuda a proteger a Grande Barreira de Corais com TensorFlow em Kaggle Junte Desafio

palavra2vec

Veja no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno

word2vec não é um algoritmo singular, mas sim uma família de arquiteturas de modelo e otimizações que podem ser usadas para aprender incorporações de palavras de grandes conjuntos de dados. As incorporações aprendidas através do word2vec provaram ser bem-sucedidas em uma variedade de tarefas de processamento de linguagem natural downstream.

Esses artigos propuseram dois métodos para aprender representações de palavras:

  • Modelo contínuo de saco de palavras : prevê a palavra do meio com base nas palavras do contexto circundante. O contexto consiste em algumas palavras antes e depois da palavra atual (do meio). Essa arquitetura é chamada de modelo bag-of-words, pois a ordem das palavras no contexto não é importante.
  • Modelo contínuo skip-gram : prevê palavras dentro de um determinado intervalo antes e depois da palavra atual na mesma frase. Um exemplo trabalhado disso é dado abaixo.

Você usará a abordagem skip-gram neste tutorial. Primeiro, você explorará os skip-grams e outros conceitos usando uma única frase para ilustração. Em seguida, você treinará seu próprio modelo word2vec em um pequeno conjunto de dados. Este tutorial também contém código para exportar os embeddings treinados e visualizá-los no TensorFlow Embedding Projector .

Skip-gram e amostragem negativa

Enquanto um modelo bag-of-words prevê uma palavra dado o contexto vizinho, um modelo skip-gram prevê o contexto (ou vizinhos) de uma palavra, dada a própria palavra. O modelo é treinado em skip-grams, que são n-grams que permitem que os tokens sejam ignorados (veja o diagrama abaixo para obter um exemplo). O contexto de uma palavra pode ser representado através de um conjunto de pares skip-gram de (target_word, context_word) onde context_word aparece no contexto vizinho de target_word .

Considere a seguinte frase de oito palavras:

A estrada larga brilhava ao sol quente.

As palavras de contexto para cada uma das 8 palavras desta frase são definidas por um tamanho de janela. O tamanho da janela determina o intervalo de palavras em cada lado de uma target_word que pode ser considerada uma context word . Abaixo está uma tabela de skip-grams para palavras-alvo com base em diferentes tamanhos de janela.

word2vec_skipgrams

O objetivo de treinamento do modelo skip-gram é maximizar a probabilidade de prever palavras de contexto dada a palavra alvo. Para uma sequência de palavras w 1 , w 2 , ... w T , o objetivo pode ser escrito como a probabilidade logarítmica média

word2vec_skipgram_objective

onde c é o tamanho do contexto de treinamento. A formulação básica do skip-gram define essa probabilidade usando a função softmax.

word2vec_full_softmax

onde v e v ' são representações de vetor de destino e contexto de palavras e W é o tamanho do vocabulário.

O cálculo do denominador dessa formulação envolve a execução de um softmax completo em todas as palavras do vocabulário, que geralmente são termos grandes (10 5 -10 7 ).

A função de perda de estimativa de contraste de ruído (NCE) é uma aproximação eficiente para um softmax completo. Com o objetivo de aprender incorporações de palavras em vez de modelar a distribuição de palavras, a perda NCE pode ser simplificada para usar amostragem negativa.

O objetivo simplificado de amostragem negativa para uma palavra alvo é distinguir a palavra de contexto de num_ns amostras negativas extraídas da distribuição de ruído P n (w) de palavras. Mais precisamente, uma aproximação eficiente de softmax completo sobre o vocabulário é, para um par skip-gram, colocar a perda de uma palavra-alvo como um problema de classificação entre a palavra de contexto e num_ns amostras negativas.

Uma amostra negativa é definida como um (target_word, context_word) de forma que a context_word não apareça na vizinhança window_size da target_word . Para a frase de exemplo, estas são algumas amostras negativas em potencial (quando window_size é 2 ).

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

Na próxima seção, você gerará skip-grams e amostras negativas para uma única frase. Você também aprenderá sobre técnicas de subamostragem e treinará um modelo de classificação para exemplos de treinamento positivos e negativos posteriormente no tutorial.

Configurar

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

Vetorizar uma frase de exemplo

Considere a seguinte frase:

A estrada larga brilhava ao sol quente.

Tokenize a frase:

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

Crie um vocabulário para salvar mapeamentos de tokens para índices inteiros:

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}

Crie um vocabulário inverso para salvar mapeamentos de índices inteiros para 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'}

Vetorize sua frase:

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

Gerar skip-grams de uma frase

O módulo tf.keras.preprocessing.sequence fornece funções úteis que simplificam a preparação de dados para word2vec. Você pode usar tf.keras.preprocessing.sequence.skipgrams para gerar pares de skip-gram a partir de example_sequence com um determinado window_size de tokens no intervalo [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

Imprima alguns 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)

Amostragem negativa para um skip-gram

A função skipgrams retorna todos os pares positivos de skip-gram deslizando sobre um determinado intervalo de janela. Para produzir pares de skip-gram adicionais que serviriam como amostras negativas para treinamento, você precisa amostrar palavras aleatórias do vocabulário. Use a função tf.random.log_uniform_candidate_sampler para amostrar o número num_ns de amostras negativas para uma determinada palavra alvo em uma janela. Você pode chamar a função na palavra de destino de um skip-grams e passar a palavra de contexto como classe verdadeira para excluí-la da amostragem.

# 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']

Construir um exemplo de treinamento

Para um determinado skip-gram positivo (target_word, context_word) , agora você também tem num_ns palavras de contexto amostradas negativas que não aparecem na vizinhança do tamanho da janela de target_word . Agrupe 1 context_word positiva e palavras de contexto negativo num_ns em um tensor. Isso produz um conjunto de skip-grams positivos (marcados como 1 ) e amostras negativas (marcadas como 0 ) para cada palavra alvo.

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

Confira o contexto e os rótulos correspondentes para a palavra-alvo do exemplo de skip-gram acima:

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]

Uma tupla de tensores (target, context, label) constitui um exemplo de treinamento para treinar seu modelo word2vec de amostragem negativa skip-gram. Observe que o destino é de forma (1,) enquanto o contexto e o rótulo são de 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)

Resumo

Este diagrama resume o procedimento de geração de um exemplo de treinamento a partir de uma frase:

word2vec_negative_sampling

Observe que as palavras temperature e code não fazem parte da frase de entrada. Eles pertencem ao vocabulário como certos outros índices usados ​​no diagrama acima.

Compile todas as etapas em uma função

Tabela de amostragem Skip-gram

Um grande conjunto de dados significa vocabulário maior com maior número de palavras mais frequentes, como palavras irrelevantes. Exemplos de treinamento obtidos da amostragem de palavras que ocorrem com frequência ( the , is , on ) não adicionam muitas informações úteis para o modelo aprender. Mikolov et ai. sugerem a subamostragem de palavras frequentes como uma prática útil para melhorar a qualidade da incorporação.

A função tf.keras.preprocessing.sequence.skipgrams aceita um argumento de tabela de amostragem para codificar probabilidades de amostragem de qualquer token. Você pode usar tf.keras.preprocessing.sequence.make_sampling_table para gerar uma tabela de amostragem probabilística baseada em classificação de frequência de palavras e passá-la para a função skipgrams . Inspecione as probabilidades de amostragem para um 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 a probabilidade de amostragem da i-ésima palavra mais comum em um conjunto de dados. A função assume uma distribuição de Zipf das frequências de palavras para amostragem.

Gerar dados de treinamento

Compile todas as etapas descritas acima em uma função que pode ser chamada em uma lista de sentenças vetorizadas obtidas de qualquer conjunto de dados de texto. Observe que a tabela de amostragem é construída antes da amostragem de pares de palavras skip-gram. Você usará essa função nas seções 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 dados de treinamento para word2vec

Com uma compreensão de como trabalhar com uma frase para um modelo word2vec baseado em amostragem negativa de skip-gram, você pode continuar a gerar exemplos de treinamento a partir de uma lista maior de frases!

Baixar corpus de texto

Você usará um arquivo de texto da escrita de Shakespeare para este tutorial. Altere a linha a seguir para executar esse código em seus próprios dados.

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

Leia o texto do arquivo e imprima as primeiras linhas:

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 as linhas não vazias para construir um objeto tf.data.TextLineDataset para as próximas etapas:

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

Vetorizar frases do corpus

Você pode usar a camada TextVectorization para vetorizar frases do corpus. Saiba mais sobre como usar essa camada neste tutorial de classificação de texto . Observe nas primeiras frases acima que o texto precisa estar em um caso e a pontuação precisa ser removida. Para fazer isso, defina uma custom_standardization function que pode ser usada na camada 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)

Chame TextVectorization.adapt no conjunto de dados de texto para criar vocabulário.

vectorize_layer.adapt(text_ds.batch(1024))

Uma vez que o estado da camada foi adaptado para representar o corpus de texto, o vocabulário pode ser acessado com TextVectorization.get_vocabulary . Esta função retorna uma lista de todos os tokens de vocabulário classificados (decrescente) por sua frequência.

# 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']

O vectorize_layer agora pode ser usado para gerar vetores para cada elemento no text_ds (um tf.data.Dataset ). Aplique Dataset.batch , Dataset.prefetch , Dataset.map e Dataset.unbatch .

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

Obter sequências do conjunto de dados

Agora você tem um tf.data.Dataset de sentenças codificadas inteiras. Para preparar o conjunto de dados para treinar um modelo word2vec, nivele o conjunto de dados em uma lista de sequências de vetores de sentença. Esta etapa é necessária, pois você itera sobre cada sentença no conjunto de dados para produzir exemplos positivos e negativos.

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

Inspecione alguns exemplos 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', '', '', '', '', '', '', '', '']

Gerar exemplos de treinamento a partir de sequências

sequences é agora uma lista de sentenças codificadas em int. Basta chamar a função generate_training_data definida anteriormente para gerar exemplos de treinamento para o modelo word2vec. Para recapitular, a função itera sobre cada palavra de cada sequência para coletar palavras de contexto positivas e negativas. O tamanho do destino, contextos e rótulos devem ser os mesmos, representando o número total de exemplos de treinamento.

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 o conjunto de dados para desempenho

Para executar lotes eficientes para o número potencialmente grande de exemplos de treinamento, use a API tf.data.Dataset . Após esta etapa, você teria um objeto tf.data.Dataset de (target_word, context_word), (label) para treinar seu 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 e Dataset.prefetch para melhorar o desempenho:

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 e treinamento

O modelo word2vec pode ser implementado como um classificador para distinguir entre palavras de contexto verdadeiro de skip-grams e palavras de contexto falso obtidas por amostragem negativa. Você pode realizar uma multiplicação de produto escalar entre as incorporações de palavras de destino e de contexto para obter previsões para rótulos e calcular a função de perda em relação a rótulos verdadeiros no conjunto de dados.

Modelo word2vec subclassificado

Use a API Keras Subclassing para definir seu modelo word2vec com as seguintes camadas:

  • target_embedding : Uma camada tf.keras.layers.Embedding , que procura a incorporação de uma palavra quando ela aparece como uma palavra de destino. O número de parâmetros nesta camada são (vocab_size * embedding_dim) .
  • context_embedding : Outra camada tf.keras.layers.Embedding , que procura a incorporação de uma palavra quando ela aparece como uma palavra de contexto. O número de parâmetros nesta camada é o mesmo que em target_embedding , ou seja (vocab_size * embedding_dim) .
  • dots : Uma camada tf.keras.layers.Dot que calcula o produto escalar de embeddings de destino e contexto de um par de treinamento.
  • flatten : Uma camada tf.keras.layers.Flatten para achatar os resultados da camada de dots em logits.

Com o modelo de subclasse, você pode definir a função call() que aceita pares (target, context) que podem ser passados ​​para sua camada de incorporação correspondente. Reformule o context_embedding para executar um produto escalar com target_embedding e retornar o resultado nivelado.

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

Definir a função de perda e compilar o modelo

Para simplificar, você pode usar tf.keras.losses.CategoricalCrossEntropy como uma alternativa à perda de amostragem negativa. Se você quiser escrever sua própria função de perda personalizada, também poderá fazê-lo da seguinte maneira:

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

É hora de construir seu modelo! Instancie sua classe word2vec com uma dimensão de incorporação de 128 (você pode experimentar com valores diferentes). Compile o modelo com o otimizador 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'])

Defina também um retorno de chamada para registrar estatísticas de treinamento para o Tensorboard:

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

Treine o modelo no conjunto de dataset por algumas é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>

O Tensorboard agora mostra a precisão e a perda do modelo word2vec:

#docs_infra: no_execute
%tensorboard --logdir logs

Incorporando pesquisa e análise

Obtenha os pesos do modelo usando Model.get_layer e Layer.get_weights . A função TextVectorization.get_vocabulary fornece o vocabulário para construir um arquivo de metadados com um token por linha.

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

Crie e salve os vetores e arquivos de metadados:

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

Baixe os vectors.tsv e metadata.tsv para analisar os embeddings obtidos no Embedding Projector :

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

Próximos passos

Este tutorial mostrou como implementar um modelo word2vec skip-gram com amostragem negativa do zero e visualizar as incorporações de palavras obtidas.