12 Extração de palavras-chave - Key term ou keyword extraction
Dado um ou mais documentos, dá se o nome de keyword - ou key phrases, key terms, key segments - extraction (KE), ou ainda keyword detection e keyword analysis à extração automatizada de termos que descrevem estes documentos, podendo ser extraídas palavras, frases ou segmentos. Além de extração de palavras-chave (keywords), a KE pode ser utilizada, por exemplo, para fazer um resumo automático de um texto. Pode ser usado para identificar palavras chave relevantes a um determinado assunto em uma massa de dados, como jornais/revistas científicas.
“Extração de palavras-chave é definida como a tarefa que identifica automaticamente um conjunto de termos que melhor descrevem o assunto do documento”
“Keyword extraction (KE) is defined as the task that automatically identifies a set of the terms that best describe the subject of document” (BELIGA, 2014)
Há quem use o termo “keyphrase” sob o argumento de que as palavras chave são compostas de uma, duas ou mais palavras. Há duas abordagens no problema da geração automática de termos: (1) atribuição de palavras chave (keyword assignment), e (2) keyword extraction. Na atribuição de palavras chave as palavras chave são definidos previamente pelo pesquisador através de um vocabulário controlado de termos, e tais termos chave não precisam estar presentes no documento. Os documentos podem então ser classificados conforme estas categorias. Já na extração de palavras chave (KE), os termos são escolhidos pelo processo de automatização e estão previamente no texto.
12.1 Atribuição de palavra chave Keyword assignment
Busca-se com a atribuição de palavra chave selecionar frases ou termos presentes em um vocabulário pré-definido pelo pesquisador.
12.2 Key term ou keyword extraction
Como dito, extração de termos ou palavras-chave consiste na extração automatizada de termos contidos no texto, sem o uso de vocabulário controlado externo. Segundo Beliga (2014) como exemplos de key word extraction temos:
- métodos estatísticos não supervisionados, como TF-IDF, KP-Miner, RAKE (Rapid Automatic Keyword Extraction).
- métodos baseados em grafos, como TextRank, SingleRank, ExpandRank TopicRank, PositionRank, TopologicalPageRank e MultipartitieRank.
- método supervisionado, como o KEA.
Além destes, pode-se destacar ainda os seguintes algoritmos de KE: Os ngrams podem ser considerados uma forma de extrair palavras chave, bem como Parts-of-Speech tagging, ou POS (por exemplo, pegando os substantivos mais frequentes), colocação e Coocorrências, PAT tree (Patricia Tree), YAKE! (Yet Another Keyword Extractor), Selectivity-Based Keyword Extraction, Yum!, GenEx (o nome é uma junção entre Genitor, um algoritmo genético e Extractor, um algoritmo de extração de palavras chave parametrizado), KeyBERT.
Os algoritmos GenEx e Kea estabeleceram a fundação dos métodos de extração de termos que vieram depois.
Dicas
- Sobre o KEA.
- I. H. Witten, G. W. Paynter, E. Frank, C. Gutwin, C. G. Nevill-Manning, “Kea: Practical Automatic Keyphrase Extraction” in Proc. of the 4th ACM Conf. of the Digital Libraries, Berkeley, CA, USA, 1999.
- resumo dos diferentes métodos de KE
O artigo que apresentou o GenEx:
- P. D. Turney, “Learning to Extract Keyphrases from Text” in Tech. Report, National Research Council of Canada, Institute for Information Technology, 1999.
- Chamamos de métodos não supervisionados aqueles que não utilizam fontes externas, utilizam somente os textos, documentos, etc.
- Chamamos de métodos supervisionados aqueles que utilizam alguma fonte externa - como modelos pré-treinados ou dicionários - para extração de palavras chave.
12.2.1 TF-IDF: Term-Frequency Inverse Document Frequency
A “frequência do termo–inverso da frequência nos documentos”, do inglês “Term-Frequency Inverse Document Frequency”, ou “TF-IDF” é utilizado para medir a relevância de palavras em uma série de documentos. Para funcionar, requer que existam vários documentos, ou textos, ou capítulos, etc. Neste algoritmo, as palavras que aparecem em todos ou em muitos documentos - como as stopwords - serão “penalizadas” e terão pontuação baixa. Agora, se uma palavra aparece bastante em um documento, mas não em outros, terá pontuação alta, e isto pode indicar que seja relevante, significativa para entender a peculiaridade daquele documento/texto. TF-IDF é útil num processo chamado de keyword extraction” ou “extração de palavras chave”.
Vamos para um exemplo:
Documento 1: “Eu quero abacaxi”
Documento 2: “Eu? Eu quero banana”
Frequência de termos, ou TF, representa a proporção que uma palavra tem no documento em questão. Esta frequência pode ser apresentada no formato de matriz.
Frequência | Doc 1 | Doc2 |
---|---|---|
Eu | 1 | 2 |
quero | 1 | 1 |
abacaxi | 1 | 0 |
banana | 0 | 1 |
total palavras | 3 | 4 |
12.2.1.1 TF: Frequência de termos (Term Frequency)
O TF de um termo que ocorre em um documento é calculado da seguinte maneira:
\(tf(t,d)\) : contagem de t(termo) em d(documento) / número de palavras no documento
O documento 1 possui 3 palavras, o documento 2 possui 4 palavras, portanto, a frequência de termos (TF) fica assim:
TF | Doc 1 | Doc2 |
---|---|---|
Eu | 1 / 3 = 0,33333 | 2 / 4 = 0,5 |
quero | 1 / 3 = 0,33333 | 1 / 4 = 0,25 |
abacaxi | 1 / 3 = 0,33333 | 0 |
banana | 0 | 1 / 4 = 0,25 |
Total | 1 | 1 |
No Documento 1, temos 3 palavras no total, cada uma, por ser única no documento, possui TF de 1/3, ou 0,33333. O Documento 2 possui 4 palavras no total. “quero” e “banana” possui um TF de 1/4 cada, ou 0,25, enquanto “eu”, que apareceu duas vezes, possui TF de 2/4 ou 0,5. TF me diz o quão frequente é uma palavra/termo em um documento. Isto pode ser feito em números absolutos bem como em termos proporcionais (bom olhar as documentações dos pacotes para entender qual o padrão utilizado)
12.2.1.2 IDF: Inverse Document Frequency
IDF, inverse document frequency, mostra o peso de um termo em relação à coleção total de documentos/textos, dando um valor baixo para termos frequentes em todos os documentos e que por isso são pouco informativos sobre as peculiaridades daquele documento, bem como privilegia termos frequentes em poucos documentos.
Assim, no nosso exemplo, as palavras “eu” e “quero” estão presentes em dois documentos de um total de dois documentos, tendo o IDF de 0. Já “abacaxi” e “banana”, termos que aparecem uma vez e somente em um documento cada, possuem IDF de 0,30102.
O cálculo é feito da seguinte forma:
Número de documentos no corpus (no caso acima, dois), dividido pelo número de documentos onde o termo aparece. Se o termo aparece uma vez somente ou 50 vezes em um documento, em ambos os casos será computado como um. O resultado disto é posto num logaritmo.
IDF | ||
---|---|---|
Eu | Log(2/2) = 0 | |
quero | Log(2/2) = 0 | |
abacaxi | Log(2/1) = 0,30102 | |
banana | Log(2/1) = 0,30102 |
Com o IDF sabemos quais termos ocorrem em vários documentos e os que ocorrem em poucos. Para saber o peso de cada termo em cada documento, usamos então o TF-IDF.
12.2.1.3 Calculando TF-IDF
TF-IDF é a multiplicação dos dois termos, TF * IDF. Ao multiplicar TF por IDF, obtemos o score da palavra no documento.
TF Doc 1 | TF Doc2 | IDF | TF-IDF Doc1 | TF-IDF Doc2 | |
---|---|---|---|---|---|
Eu | 1 / 3 = 0,33333 | 2 / 4 = 0,5 | Log(2/2) = 0 | 0,33333 * 0 = 0 | 0,5 * 0 = 0 |
quero | 1 / 3 = 0,33333 | 1 / 4 = 0,25 | Log(2/2) = 0 | 0,33333 * 0 = 0 | 0,5 * 0 = 0 |
abacaxi | 1 / 3 = 0,33333 | 0 | Log(2/1) = 0,301 | 0,3 * 0,3 = 0,1003 | 0 * 0,3 = 0 |
banana | 0 | 1 / 4 = 0,25 | Log(2/1) = 0,301 | 0 * 0,3 = 0 | 0,25 * 0,3 = 0,0752 |
Matemática
Esta é a fórmula do TF-IDF, e nos retorna o índice TF-IDF para cada palavra em cada documento.
\(W_{ij} = tf_{i,j} \times \log(\frac{N}{df_i})\)
Destrinchando a fórmula:
Fórmula | Descrição |
---|---|
\(W_{ij}\) | um termo \(i\) num documento \(j\), para o qual vamos calcular o TF-IDF |
\(tf_{i,j}\) | frequência do termo \(i\), no documento \(j\) |
\(df_{ij}\) | Número de documentos que contenham o termo \(i\). Pouco importa se aparece apenas uma vez ou se 500 vezes num mesmo documento, seu valor em cada documento, se presente, é 1 |
\(N\) | Número total de documentos |
\(df_i\) | frequência de documentos que contenham o termo \(i\) |
Se o resultado encontrado se aproximar de “0”, então a palavra se encontra presente em vários documentos. Caso contrário, quanto mais se aproxima de “1”, mais rara é esta palavra em outros documentos e mais concentrada em poucos documentos. Vale ressaltar que o cálculo TF-IDF pode ser feito tanto com a frequência absoluta ou como com a relativa.
Vimos exemplo de TF-IDF com apenas dois “documentos”. Vamos usar mais documentos para entender melhor o TF-IDF. Usamos um pacote de R nos bastidores para gerar a tabela à seguir, mas veremos o código que o gerou mais à frente.
<- "Eu quero abacaxi!"
doc1 <- "Eu quero açaí!"
doc2 <- "Eu quero manga ou açaí! Eu quero manga! Manga!" doc3
O exemplo acima possui diferentes configurações de palavras para observarmos o TF-IDF:
- “eu” e “quero” em todos os docs
- “abacaxi”, “manga” e “ou” que ocorrem uma vez.
- “açaí” que ocorre uma vez em dois documentos diferentes.
- “manga” que aparece várias vezes em um documento somente.
## Document-feature matrix of: 3 documents, 6 features (38.89% sparse) and 0 docvars.
## features
## docs eu quero abacaxi açaí manga ou
## text1 0 0 0.1590404 0 0 0
## text2 0 0 0 0.05869709 0 0
## text3 0 0 0 0.01956570 0.1590404 0.05301347
- “eu” e “quero” ocorrem em todos os docs e possuem TF-IDF de 0 em todos os casos.
- “abacaxi” aparece somente na primeira frase e tem TF-IDF de 0.1590404
- “açaí” ocorre uma vez em dois documentos possui TF-IDF em um doc com menos palavras no total.
- “ou” e “manga” só aparecem na frase 3, e manga aparece 3 vezes e tem TF-IDF maior que a palavra “ou”, que só aparece uma vez.
- No doc3, “ou” (que só aparece uma vez em um doc) possui TF-IDF maior que “açaí”, que aparece em mais de um doc.
Podemos realizar o TF-IDF no R calculando manualmente, como neste exemplo em video ou neste tutorial, ou podemos usar alguns dos vários pacotes que tem já implementadas a função, como o Tidytext, Quanteda e TM, que veremos a seguir. Vale atentar que cálculos feitos com diferentes pacotes podem não bater entre si. Se for este o caso, atente para se usam frequência absoluta ou relativa, e qual a base do Logaritmo utilizado (se de base 2 ou 10).
Em um exemplo real de uso de TF-IDF, este tutorial usou TF-IDF entre diferentes livros do Harry Potter:
Fonte: Text Mining: Term vs. Document Frequency do AFIT Data Science Lab R Programming Guide
12.2.1.4 TF-IDF no R: Tidyverse
Podemos realizar o TF-IDF no R com o tidytext com a função tidytext::bind_tf_idf.
<- "Eu quero abacaxi"
doc1 <- "Eu? Eu quero banana"
doc2 # criando o data frame, onde cada linha é um documento.
<- data.frame("texto" = c(doc1,doc2),
df # ID de "identificação"
"ID" = c(1,2),
stringsAsFactors = F)
%>%
df # quebrando o texto em tokens
::unnest_tokens(output = 'word', token = 'words',
tidytext# input = nome da coluna do dataframe
input = texto) %>%
# contando os termos
::count(ID, word, sort = TRUE) %>%
dplyr# TF-IDF
::bind_tf_idf(word, ID, n)
tidytext## ID word n tf idf tf_idf
## 1 2 eu 2 0.5000000 0.0000000 0.0000000
## 2 1 abacaxi 1 0.3333333 0.6931472 0.2310491
## 3 1 eu 1 0.3333333 0.0000000 0.0000000
## 4 1 quero 1 0.3333333 0.0000000 0.0000000
## 5 2 banana 1 0.2500000 0.6931472 0.1732868
## 6 2 quero 1 0.2500000 0.0000000 0.0000000
Em bind_tf_idf()
sendo: word
a coluna contendo termos, ID
a coluna contendo os IDs dos docs, n
a contagem de palavras produzido por count()
.
Dicas
Exemplo/tutorial de TF-IDF com o tidyverse de Julia Silge e David Robinson.
12.2.1.5 TF-IDF no R: Quanteda
TF-IDF com o pacote Quanteda é usado com a função quanteda::dfm_tfidf
<- "Eu quero Elias"
doc1 <- "Eu? Eu quero Durkheim"
doc2
# criando um vetor com documentos para transformar em um corpus no quanteda
<- c(doc1,doc2)
meuvetor # criando um objeto tipo corpus a partir do vetor
<- quanteda::corpus(meuvetor)
meucorpus
# criando uma matriz de frequência
<- meucorpus %>%
meudfm # quebrando em tokens
::tokens(
quanteda# removendo a pontuação
remove_punct = TRUE) %>%
# transformando em um document frame matrix
::dfm()
quanteda
meudfm## Document-feature matrix of: 2 documents, 4 features (25.00% sparse) and 0 docvars.
## features
## docs eu quero elias durkheim
## text1 1 1 1 0
## text2 2 1 0 1
# gerando o tf-idf (frequencia absoluta)
::dfm_tfidf(meudfm)
quanteda## Document-feature matrix of: 2 documents, 4 features (25.00% sparse) and 0 docvars.
## features
## docs eu quero elias durkheim
## text1 0 0 0.30103 0
## text2 0 0 0 0.30103
# TF-IDF usando frequência relativa, proporcional
::dfm_tfidf(meudfm, scheme_tf = "prop")
quanteda## Document-feature matrix of: 2 documents, 4 features (25.00% sparse) and 0 docvars.
## features
## docs eu quero elias durkheim
## text1 0 0 0.1003433 0
## text2 0 0 0 0.0752575
quanteda::dfm_tfidf(x, scheme_tf = “count”, scheme_df = “inverse”, base = 2)
Parâmetro | Descrição |
---|---|
x | objeto de entrada, devendo ser um document-feature matrix |
scheme_tf | esquema para dfm_weight(); sendo o padrão “count”. Para usar a frequência relativa, usa-se o “prop” |
scheme_df | esquema para docfreq(); sendo o padrão “inverse”. |
base | A base para logaritmo no dfm_weight() e docfreq(), sendo 10 o valor padrão. Outro valor comum é 2 |
12.2.1.6 TF-IDF no R: TM
Vamos usar agora o pacote TM.
<- "Eu quero Durkheim"
doc1 <- "Elias! Eu quero Elias"
doc2
<- c(doc1,doc2)
vetor_vetores
# criando o objeto tipo corpus para o TM
<- tm::Corpus(tm::VectorSource(vetor_vetores))
meu_corpus # observando a estrutura do objeto criado, que é uma lista
str(meu_corpus)
## Classes 'SimpleCorpus', 'Corpus' hidden list of 3
## $ content: chr [1:2] "Eu quero Durkheim" "Elias! Eu quero Elias"
## $ meta :List of 1
## ..$ language: chr "en"
## ..- attr(*, "class")= chr "CorpusMeta"
## $ dmeta :'data.frame': 2 obs. of 0 variables
Vamos a um pré-processamento do pacote tm
com a função tm_map()
e tm::removePunctuation.
<-
meu_corpus2 # passando tudo para minúsculo
::tm_map(meu_corpus, tolower) %>%
tm# removendo pontuações
::tm_map(., tm::removePunctuation)
tm## Warning in tm_map.SimpleCorpus(meu_corpus, tolower): transformation drops
## documents
## Warning in tm_map.SimpleCorpus(., tm::removePunctuation): transformation drops
## documents
meu_corpus2## <<SimpleCorpus>>
## Metadata: corpus specific: 1, document level (indexed): 0
## Content: documents: 2
A matriz de termo por documento (document-term-matrix) computa quantas vezes um termo aparece por documento, que no nosso caso foi uma frase simples. “Durkheim” aparece uma vez no documento 1, “quero” aparece uma vez em cada documento e “Elias” aparece duas vezes no documento 2.
Opção 1, mais simples
<- tm::DocumentTermMatrix(meu_corpus2,
dtm.tfidf control = list(weighting = tm::weightTfIdf))
## Warning in TermDocumentMatrix.SimpleCorpus(x, control): custom functions are
## ignored
# vendo a estrutura
dtm.tfidf## <<DocumentTermMatrix (documents: 2, terms: 3)>>
## Non-/sparse entries: 2/4
## Sparsity : 67%
## Maximal term length: 8
## Weighting : term frequency - inverse document frequency (normalized) (tf-idf)
# Para visualizar, transformamos nosso objeto em matriz
as.matrix(dtm.tfidf)
## Terms
## Docs durkheim quero elias
## 1 0.5 0 0.0000000
## 2 0.0 0 0.6666667
Opção 2: com normalização e retirada de stopwords
<- "Eu quero Durkheim"
doc1 <- "Elias! Eu quero Elias"
doc2
<- c(doc1,doc2)
vetor.docs
# criando o objeto tipo corpus para o TM
# como nossa fonte são vetores, usamos VectorSource
<- tm::Corpus(tm::VectorSource(vetor.docs))
meu_corpus
::DocumentTermMatrix(meu_corpus,
tm# para concatenar várias transformacoes, vamos usar function
control = list(weighting = function(x)
::weightTfIdf(x, normalize = T),
tmremovePunctuation = TRUE,
stopwords = TRUE)) %>% as.matrix
## Warning in TermDocumentMatrix.SimpleCorpus(x, control): custom functions are
## ignored
## Terms
## Docs durkheim quero elias
## 1 0.5 0 0.0000000
## 2 0.0 0 0.6666667
Dicas TF-IDF
- JONES, Karen Spärck. A statistical interpretation of term specificity and its application in retrieval. Journal of Documentation. (1972)
- Cap. 6.5 TF-IDF: Weighing terms in the vector in JURAFSKI,D; MARTIN,J. Speech and Language Processing.
- Gerard Salton and Christopher Buckley (1988). Term-weighting approaches in automatic text retrieval. Information Processing and Management, 24/5, 513-523.
Tutorial
- vignette (exemplo) de TF-IDF com o tidytext
- Análise tf-idf com livros do Harry Potter usando o Tidyverse.
Vídeos:
- Video em português Aula 5.4: Problema com Matrizes de Frequência e TF-IDF | Processamento de Língua Natural. do curso de “Processamento de Língua Natural” (LIG948B), ministrado na Faculdade de Letras da Universidade Federal de Minas Gerais (FALE-UFMG) em Python.
- Video tutorial de tf-idf no R (em inglês) “TF-IDF | Introduction to Text Analytics with R Part 5” do Data Science Dojo, fazendo o cálculo sem pacotes.
12.3 Colocação e Coocorrência
Uma das vantagens de
tokenizers, em relação à grande maioria dos tokenizadores - exceção ao Quanteda - é a possibilidade de criar ngrams de diferentes tamanhos, ao mesmo tempo, alterando apenas um parâmetro, sem demandar novas linhas de código.
Esta facilidade se encontra também no pacote Quanteda (ambos pacotes possuem criadores em comum).
Isto é chamado de “multi-word units” {#multiword_units}, ou “colocação” (collocation”) {#colocation} na linguística.
São tokens que aparecem juntos muito frequentemente, como em nomes próprios ou junções frequentes entre substantivo e Por exemplo, “Minas Gerais”, “Machine Learning”, “ódio mortal”, “cena do crime”.
É útil considerá-los como uma palavra só;
O Quanteda, além de também realizar a tokenização “multi words”, oferece também as funções textstat_collocations()
que nos retorna uma tabela com cáculo estatístico identificando colocações e a função tokens_compound()
que adiciona um underscore entre as palavras da colocação, de modo que formem um único vocábulo, por exemplo, “machine_learning”, “New_York”.
<- 'A democracia no Brasil foi sempre um lamentável mal-entendido. Uma aristocracia rural e semifeudal importou-a e tratou de acomodá-la, onde fosse possível, aos seus direitos ou privilégios, os mesmos privilégios que tinham sido, no Velho Mundo, o alvo da luta da burguesia contra os aristocratas. E assim puderam incorporar à situação tradicional, ao menos como fachada ou decoração externa, alguns lemas que pareciam os mais acertados para a época e eram exaltados nos livros e discursos. É curioso notar-se que os movimentos aparentemente reformadores, no Brasil, partiram quase sempre de cima para baixo: foram de inspiração intelectual, se assim se pode dizer, tanto quanto sentimental. Nossa independência, as conquistas liberais que fizemos durante o decurso de nossa evolução política vieram quase de surpresa; a grande massa do povo recebeu-as com displicência, ou hostilidade. Não emanavam de uma predisposição espiritual e emotiva particular, de uma concepção da vida bem definida e específica, que tivesse chegado à maturidade plena. Os campeões das novas idéias esqueceram-se, com freqüência, de que as formas de vida nem sempre são expressões do arbítrio pessoal, não se "fazem" ou "desfazem" por decreto. A célebre carta de Aristides Lobo sobre o 15 de Novembro é documento flagrante do imprevisto que representou para nós, a despeito de toda a propaganda, de toda a popularidade entre os moços das academias, a realização da idéia republicana. "Por ora", dizia o célebre paredro do novo regime, "por ora a cor do governo é puramente militar e deverá ser assim. O fato foi deles, deles só, porque a colaboração de elemento civil foi quase nula. O povo assistiu àquilo bestializado, atônito, surpreso, sem conhecer o que significava.' txt.raizesBr
12.3.1 Colocação com o Quanteda
Vejamos um exemplo:
<- data_corpus_inaugural[1:2]
corp head(cols <- quanteda.textstats::textstat_collocations(corp, size = 2, min_count = 2), 10)
## collocation count count_nested length lambda z
## 1 have been 5 0 2 5.704259 7.354588
## 2 has been 3 0 2 5.565217 6.409333
## 3 of the 24 0 2 1.673501 6.382475
## 4 i have 5 0 2 3.743580 6.268303
## 5 which i 6 0 2 3.172217 6.135144
## 6 will be 4 0 2 3.868500 5.930143
## 7 less than 2 0 2 6.279494 5.529680
## 8 public good 2 0 2 6.279494 5.529680
## 9 you will 2 0 2 4.917893 5.431752
## 10 may be 3 0 2 4.190711 5.328038
head(cols <- quanteda.textstats::textstat_collocations(txt.raizesBr, size = 2, min_count = 2), 10)
## collocation count count_nested length lambda z
## 1 no brasil 2 0 2 6.626718 3.781401
## 2 por ora 2 0 2 6.626718 3.781401
## 3 toda a 2 0 2 5.770551 3.518257
## 4 de toda 2 0 2 4.456670 2.827344
## 5 de uma 2 0 2 4.456670 2.827344
::textstat_collocations(txt.raizesBr, size = 2, min_count = 2)
quanteda.textstats## collocation count count_nested length lambda z
## 1 no brasil 2 0 2 6.626718 3.781401
## 2 por ora 2 0 2 6.626718 3.781401
## 3 toda a 2 0 2 5.770551 3.518257
## 4 de toda 2 0 2 4.456670 2.827344
## 5 de uma 2 0 2 4.456670 2.827344
EM EXPANSÃO Este trecho será expandido
12.3.2 Colocação com Udpipe
EM EXPANSÃO Este trecho será expandido
Manning, C. D., Raghavan, P., & Schütze, H. Introduction to Information Retrieval. Cambridge: Cambridge University Press. 2008.
Dicas
- Video no YouTube OpenAI Outperforms Some Humans In Article Summarization! sobre um algoritmo de sumarização de textos, de 30 de março de 2021 sobre o paper “Learning to Summarize with Human Feedback”.. September 4, 2020.