Posit AI Weblog: Phrase Incoreddings com Keras



Posit AI Weblog: Phrase Incoreddings com Keras

Introdução

A incorporação de palavras é um método usado para mapear palavras de um vocabulário para densos vetores de números reais, onde palavras semanticamente semelhantes são mapeadas para pontos próximos. Representar as palavras neste espaço vetorial Ajuda os algoritmos alcançam um melhor desempenho em tarefas de processamento de linguagem pure, como análise sintática e análise de sentimentos, agrupando palavras semelhantes. Por exemplo, esperamos que, no espaço de incorporação, “gatos” e “cães” sejam mapeados para pontos próximos, já que ambos são animais, mamíferos, animais de estimação, and many others.

Neste tutorial, implementaremos o modelo Skip-Gram criado por Mikolov et al em r usando o Keras pacote. O modelo Skip-Gram é um sabor do Word2Vec, uma classe de modelos preditivos computacionalmente eficientes para aprender incorporações de palavras a partir de texto bruto. Não abordaremos detalhes teóricos sobre incorporações e o modelo de pular grama. Se você deseja obter mais detalhes, pode ler o artigo vinculado acima. O tensorflow Representação vetorial de palavras O tutorial inclui detalhes adicionais, assim como o Aprendizado profundo com r Caderno sobre incorporação.

Existem outras maneiras de criar representações vetoriais de palavras. Por exemplo, as incorporações de luvas são implementadas no text2vec Pacote de Dmitriy Selivanov. Há também uma abordagem arrumada descrita na postagem do weblog de Julia Silge Vetores de palavras com princípios de dados arrumados.

Obtendo os dados

Vamos usar o Amazon Fantastic Meals Evaluations DataSet. Este conjunto de dados consiste em revisões de alimentos finos da Amazon. Os dados abrangem um período de mais de 10 anos, incluindo todas as ~ 500.000 revisões até outubro de 2012. As análises incluem informações de produtos e usuários, classificações e texto narrativo.

Os dados podem ser baixados (~ 116 MB) em execução:

obtain.file("https://snap.stanford.edu/knowledge/finefoods.txt.gz", "finefoods.txt.gz")

Agora vamos carregar as avaliações de texto sem formatação em R.

Vamos dar uma olhada em alguns comentários que temos no conjunto de dados.

(1) "I've purchased a number of of the Vitality canned pet food merchandise ...
(2) "Product arrived labeled as Jumbo Salted Peanuts...the peanuts ... 

Pré -processamento

Começaremos com algum pré-processamento de texto usando um Keras text_tokenizer(). O Tokenizer será responsável por transformar cada revisão em uma sequência de tokens inteiros (que será posteriormente usado como entrada no modelo Skip-Gram).

library(keras)
tokenizer <- text_tokenizer(num_words = 20000)
tokenizer %>% fit_text_tokenizer(critiques)

Observe que o tokenizer o objeto é modificado em vigor pela chamada para fit_text_tokenizer(). Um token inteiro será atribuído a cada uma das 20.000 palavras mais comuns (as outras palavras serão atribuídas ao token 0).

Modelo de pular grama

No modelo Skip-Gram, usaremos cada palavra como entrada para um classificador de log-linear com uma camada de projeção e preverá palavras dentro de um determinado intervalo antes e depois dessa palavra. Seria muito caro computacionalmente emitir uma distribuição de probabilidade sobre todo o vocabulário para cada palavra de destino que inserimos no modelo. Em vez disso, usaremos amostragem negativa, o que significa que provaremos algumas palavras que não aparecem no contexto e treinaremos um classificador binário para prever se a palavra de contexto que passamos é realmente do contexto ou não.

Em termos mais práticos, para o modelo Skip-Gram, inseriremos um vetor inteiro 1D dos tokens de palavra-alvo e um vetor inteiro 1D de tokens de palavras de contexto amostrado. Geraremos uma previsão de 1 se a palavra amostrada realmente aparecer no contexto e 0 se não o fizesse.

Agora definiremos uma função de gerador para produzir lotes para treinamento de modelos.

library(reticulate)
library(purrr)
skipgrams_generator <- operate(textual content, tokenizer, window_size, negative_samples) {
  gen <- texts_to_sequences_generator(tokenizer, pattern(textual content))
  operate() {
    skip <- generator_next(gen) %>%
      skipgrams(
        vocabulary_size = tokenizer$num_words, 
        window_size = window_size, 
        negative_samples = 1
      )
    x <- transpose(skip${couples}) %>% map(. %>% unlist %>% as.matrix(ncol = 1))
    y <- skip$labels %>% as.matrix(ncol = 1)
    checklist(x, y)
  }
}

UM Função do gerador
é uma função que retorna um valor diferente cada vez que é chamado (as funções do gerador são frequentemente usadas para fornecer dados de streaming ou dinâmico para modelos de treinamento). Nossa função do gerador receberá um vetor de textos, um tokenizador e os argumentos para o pular grama (o tamanho da janela em torno de cada palavra de destino que examinamos e quantas amostras negativas queremos amostrar para cada palavra de destino).

Agora vamos começar a definir o modelo Keras. Vamos usar as keras API funcional.

embedding_size <- 128  # Dimension of the embedding vector.
skip_window <- 5       # What number of phrases to contemplate left and proper.
num_sampled <- 1       # Variety of damaging examples to pattern for every phrase.

Primeiro, escreveremos espaços reservados para os insumos usando o layer_input função.

input_target <- layer_input(form = 1)
input_context <- layer_input(form = 1)

Agora vamos definir a matriz de incorporação. A incorporação é uma matriz com dimensões (vocabulário, incorporar_size) que atua como tabela de pesquisa para os vetores da palavra.

embedding <- layer_embedding(
  input_dim = tokenizer$num_words + 1, 
  output_dim = embedding_size, 
  input_length = 1, 
  title = "embedding"
)

target_vector <- input_target %>% 
  embedding() %>% 
  layer_flatten()

context_vector <- input_context %>%
  embedding() %>%
  layer_flatten()

O próximo passo é definir como o target_vector estará relacionado ao context_vector
Para fazer com que nossa rede saia 1 quando a palavra de contexto realmente apareceu no contexto e 0 de outra forma. Nós queremos target_vector ser semelhante para o context_vector
Se eles apareceram no mesmo contexto. Uma medida típica de similaridade é o similaridade de cosseno. Dê dois vetores (UM) e (B )
A similaridade de cosseno é definida pelo produto de ponto euclidiano de (UM) e (B ) normalizado por sua magnitude. Como não precisamos da semelhança para ser normalizada dentro da rede, calcularemos apenas o produto DOT e, em seguida, produziremos uma camada densa com ativação sigmóide.

dot_product <- layer_dot(checklist(target_vector, context_vector), axes = 1)
output <- layer_dense(dot_product, models = 1, activation = "sigmoid")

Agora criaremos o modelo e o compilamos.

mannequin <- keras_model(checklist(input_target, input_context), output)
mannequin %>% compile(loss = "binary_crossentropy", optimizer = "adam")

Podemos ver a definição completa do modelo ligando abstract:

_________________________________________________________________________________________
Layer (kind)                 Output Form       Param #    Related to                  
=========================================================================================
input_1 (InputLayer)         (None, 1)          0                                        
_________________________________________________________________________________________
input_2 (InputLayer)         (None, 1)          0                                        
_________________________________________________________________________________________
embedding (Embedding)        (None, 1, 128)     2560128    input_1(0)(0)                 
                                                           input_2(0)(0)                 
_________________________________________________________________________________________
flatten_1 (Flatten)          (None, 128)        0          embedding(0)(0)               
_________________________________________________________________________________________
flatten_2 (Flatten)          (None, 128)        0          embedding(1)(0)               
_________________________________________________________________________________________
dot_1 (Dot)                  (None, 1)          0          flatten_1(0)(0)               
                                                           flatten_2(0)(0)               
_________________________________________________________________________________________
dense_1 (Dense)              (None, 1)          2          dot_1(0)(0)                   
=========================================================================================
Whole params: 2,560,130
Trainable params: 2,560,130
Non-trainable params: 0
_________________________________________________________________________________________

Treinamento modelo

Vamos ajustar o modelo usando o fit_generator() Função Precisamos especificar o número de etapas de treinamento, bem como o número de épocas que queremos treinar. Vamos treinar para 100.000 etapas para 5 épocas. Isso é bastante lento (~ 1000 segundos por época em uma GPU moderna). Observe que você também pode obter resultados razoáveis com apenas uma época de treinamento.

mannequin %>%
  fit_generator(
    skipgrams_generator(critiques, tokenizer, skip_window, negative_samples), 
    steps_per_epoch = 100000, epochs = 5
    )
Epoch 1/1
100000/100000 (==============================) - 1092s - loss: 0.3749      
Epoch 2/5
100000/100000 (==============================) - 1094s - loss: 0.3548     
Epoch 3/5
100000/100000 (==============================) - 1053s - loss: 0.3630     
Epoch 4/5
100000/100000 (==============================) - 1020s - loss: 0.3737     
Epoch 5/5
100000/100000 (==============================) - 1017s - loss: 0.3823 

Agora podemos extrair a matriz de incorporação do modelo usando o get_weights()
função. Nós também adicionamos row.names para nossa matriz de incorporação para que possamos encontrar facilmente onde está cada palavra.

Compreendendo as incorporações

Agora podemos encontrar palavras próximas uma da outra na incorporação. Usaremos a similaridade do cosseno, pois foi isso que treinamos o modelo para minimizar.

library(text2vec)

find_similar_words <- operate(phrase, embedding_matrix, n = 5) {
  similarities <- embedding_matrix(phrase, , drop = FALSE) %>%
    sim2(embedding_matrix, y = ., technique = "cosine")
  
  similarities(,1) %>% type(lowering = TRUE) %>% head(n)
}
find_similar_words("2", embedding_matrix)
        2         4         3       two         6 
1.0000000 0.9830254 0.9777042 0.9765668 0.9722549 
find_similar_words("little", embedding_matrix)
   little       bit       few     small     deal with 
1.0000000 0.9501037 0.9478287 0.9309829 0.9286966 
find_similar_words("scrumptious", embedding_matrix)
scrumptious     tasty great   superb     yummy 
1.0000000 0.9632145 0.9619508 0.9617954 0.9529505 
find_similar_words("cats", embedding_matrix)
     cats      canine      children       cat       canine 
1.0000000 0.9844937 0.9743756 0.9676026 0.9624494 

O t-sne O algoritmo pode ser usado para visualizar as incorporações. Devido às restrições de tempo, usaremos apenas com as primeiras 500 palavras. Para entender mais sobre o t-sne Método Consulte o artigo Como usar o T-SNE efetivamente.

Esse enredo pode parecer uma bagunça, mas se você ampliar os pequenos grupos, acaba vendo alguns padrões agradáveis. Tente, por exemplo, encontrar um grupo de palavras relacionadas à Net como httpAssim, hrefand many others. Outro grupo que pode ser fácil de escolher é o grupo de pronomes: sheAssim, heAssim, herand many others.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *