Detecção de plágio com NLP no R: o caso Decotelli

Natural Language Processing (NLP) é um tópico interessante que vem sendo bastante explorado no mundo da ciência de dados. Aqui no blog já exploramos, por exemplo, a mineração de textos (text mining) das atas do COPOM (códigos disponíveis em R). No texto de hoje exploraremos mais o assunto aplicado ao problema de detecção de plágio entre dois ou mais textos, usando como exemplo um evento repercutido no ano passado envolvendo o ex-Ministro da Educação, Carlos Decotelli.

Para contextualizar, e para quem não teve conhecimento do evento em questão, em junho de 2020 houveram apontamentos de que a dissertação de mestrado do então Ministro da Educação, Carlos Decotelli, continha indícios de plágio. Muitas notícias foram vinculadas na época e após o “escândalo” o Ministro entregou uma carta de demissão.

Dados os atores envolvidos e repercussão deste evento, é natural aos cientistas de dados se perguntarem se o plágio pode ser verificado de forma mais empírica. Neste texto faremos um exercício aplicado de R para tentar responder essa questão, usando o pacote textreuse.

O pacote textreuse serve muito bem o propósito deste exercício, pois fornece um conjunto de funções para medir a similaridade entre documentos de texto e detectar passagens que foram reutilizadas. Para realizar essa mensuração existem algumas possibilidades, entre elas o Índice de Jaccard - também conhecido como coeficiente de similaridade de Jaccard - que oferece uma estatística para medir a similaridade entre conjuntos amostrais, expresso conforme abaixo:

O Índice de Jaccard é muito simples quando queremos identificar similaridade entre textos, sendo os coeficientes expressos como números entre 0 e 1. Dessa forma, quanto maior o número, mais semelhantes são os textos (conjuntos amostrais).

Agora vamos à prática!

Pacotes

Para reproduzir os código de R deste exercício, certifique de que tenha os seguintes pacotes instalados/carregados:


# Carregar pacotes
library(textreuse) # CRAN v0.1.5
library(magrittr) # CRAN v2.0.1
library(dplyr) # CRAN v1.0.7
library(stringr) # CRAN v1.4.0
library(ggplot2) # CRAN v3.3.5
library(ggtext) # CRAN v0.1.1
library(scales) # CRAN v1.1.1

Dados

Os dados utilizados são os documentos que queremos comparar a similaridade. O principal documento é a dissertação de mestrado de Carlos Decotelli, e os demais são documentos indicados por Thomas Conti de onde os trechos teriam sido copiados. Nos links à seguir você pode baixar os originais de cada documento:

  1. Dissertação Carlos Decotelli (2008): https://bibliotecadigital.fgv.br/dspace/handle/10438/3726
  2. Relatório CVM (2007) - The Internet Archive: http://web.archive.org/web/20200629224030/https://ri.banrisul.com.br/banrisul/web/arquivos/Banrisul_DFP2007b_port.pdf
  3. “A Abordagem Institucional na Administração” de Alexandre Rosa e Cláudia Coser (2003): https://twiki.ufba.br/twiki/bin/viewfile/PROGESP/ItemAcervo241?rev=&filename=Abordagem_institucional_na_administracao.pdf
  4. “Mudança e estratégia nas organizações” de Clóvis L. Machado-da-Silva et al. (1999): http://anpad.org.br/admin/pdf/enanpad1998-orgest-26.pdf

Para usar esses documentos no pacote textreuse precisamos transformá-los para o formato de texto (extensão .txt). Existem formas de fazer isso por meio de pacotes no R, no entanto, para simplificar eu usei ferramentas online que fazem esse serviço, tomando o cuidado de remover as páginas pré ou pós textuais (capa, sumário, referências, etc.) antes da conversão, já que o objetivo é comparar os textos do corpo dos documentos propriamente ditos.

Ferramenta online para converter de PDF para TXT: https://pdftotext.com/pt/

Após esse tratamento/conversão teremos 4 arquivos de texto para realizar a análise. Disponibilizei os arquivos neste link para quem se interessar em reproduzir. Na sequência importamos os arquivos para o R:


# Criar pasta temporária
temp_folder <- tempdir()

# Baixar arquivos txt (zipados)
download.file(
url = "https://aluno.analisemacro.com.br/download/34322/",
destfile = paste0(temp_folder, "\\decotelli.zip"),
mode = "wb" # talvez você precise mudar esse argumento, dependendo do seu sistema
)

# Descompactar
unzip(
zipfile = paste0(temp_folder, "\\decotelli.zip"),
exdir = paste0(temp_folder, "txts")
)

# Listar arquivos na pasta
list.files(paste0(temp_folder, "txts"), pattern = ".txt")

# [1] "clovis-p ginas-1-13.txt" "cvm-p ginas-23-62.txt"
# [3] "decotelli-p ginas-9-83.txt" "rosa-p ginas-1-13.txt"

# Importar arquivos (se arquivos não tiverem marcador "End of Line" será gerado um warning
corpus <- textreuse::TextReuseCorpus( # cria uma estrutura Corpus (coleção de documentos)
dir = paste0(temp_folder, "txts"), # caminho para pasta com arquivos txt
tokenizer = tokenize_ngrams, # função para separar textos em tokens
progress = FALSE # não mostrar barra de progresso
)

Se você tiver dificuldades em importar os dados, é possível explorar os exemplos das Vignettes do pacote também.

Agora vamos inspecionar o que temos no objeto corpus:


# Exibir resultado
corpus

# TextReuseCorpus
# Number of documents: 4
# hash_func : hash_string
# tokenizer : tokenize_ngrams

Verificando a classe do objeto:


# Classe
class(corpus)

# [1] "TextReuseCorpus" "Corpus"

Quais documentos/textos temos no objeto corpus?


# Documentos importados
names(corpus)

# [1] "clovis-p ginas-1-13" "cvm-p ginas-23-62" "decotelli-p ginas-9-83"
# [4] "rosa-p ginas-1-13"

E, similarmente, podemos acessar os elementos do objeto:


# Acessando texto da dissertação de mestrado
corpus[[3]]

# TextReuseTextDocument
# file : C:\Users\ferna\AppData\Local\Temp\Rtmp6rA1wGtxts/decotelli-p ginas-9-83.txt
# hash_func : hash_string
# id : decotelli-p ginas-9-83
# minhash_func :
# tokenizer : tokenize_ngrams
# content : 1 – INTRODUÇÃO
#
# A moeda é sem dúvida a principal invenção do homem na área das ciências sociais. Criada para
# facilitar as trocas, viabilizando a especialização do trabalho, a moeda é de s

Perceba que há problemas de encoding, que para este exercício iremos simplesmente ignorar.

Índice de Jaccard

Com os dados preparados, podemos obter a estatística do Índice de Jaccard para medir a similaridade entre os textos, usando a função jaccard_similarity(), que compara um texto A com um texto B. Queremos fazer isso já para os 4, portanto usamos a função auxiliar pairwise_compare() que reporta a estatística de cada documento comparando a todos os demais (todos os pares possíveis), dentro do objeto corpus:


# Coeficientes de similaridade de Jaccard
comparisons <- textreuse::pairwise_compare(
corpus = corpus, # objeto com textos importados
f = jaccard_similarity # função para comparar os textos
)

# Resultado
comparisons

# clovis-p ginas-1-13 cvm-p ginas-23-62
# clovis-p ginas-1-13 NA 0.01308411
# cvm-p ginas-23-62 NA NA
# decotelli-p ginas-9-83 NA NA
# rosa-p ginas-1-13 NA NA
# decotelli-p ginas-9-83 rosa-p ginas-1-13
# clovis-p ginas-1-13 0.04087225 0.02439457
# cvm-p ginas-23-62 0.19385437 0.01169428
# decotelli-p ginas-9-83 NA 0.03302505
# rosa-p ginas-1-13 NA NA

O resultado de destaque mostra que o texto da dissertação de mestrado de Carlos Decotelli (decotelli-p ginas-9-83) comparado com o texto do relatório da CVM (cvm-p ginas-23-62) obteve um coeficiente de 0.19 (o valor máximo vai até 1), bem superior aos demais. Esse resultado dialoga com o que foi indicado por Thomas Conti em junho de 2020.

Vale frisar que existem outros métodos de comparação e funcionalidades que o pacote oferece, consulte a documentação.

Podemos facilmente converter o resultado, que é uma matriz, para um data frame com a função pairwise_candidates():


# Converter para data frame
comparisons_df <- textreuse::pairwise_candidates(comparisons)
comparisons_df

# A tibble: 6 x 3
# a b score
# * <chr> <chr> <dbl>
# 1 clovis-p ginas-1-13 cvm-p ginas-23-62 0.0131
# 2 clovis-p ginas-1-13 decotelli-p ginas-9-83 0.0409
# 3 clovis-p ginas-1-13 rosa-p ginas-1-13 0.0244
# 4 cvm-p ginas-23-62 decotelli-p ginas-9-83 0.194
# 5 cvm-p ginas-23-62 rosa-p ginas-1-13 0.0117
# 6 decotelli-p ginas-9-83 rosa-p ginas-1-13 0.0330

Por fim, vamos criar uma visualização dos resultados:


comparisons_df %>%
dplyr::filter(
stringr::str_detect(string = a, "decotelli") |
stringr::str_detect(string = b, "decotelli")
) %>%
dplyr::mutate(a = c("Machado-da-Silva et al.", "Relatório CVM", "Rosa & Coser")) %>%
ggplot2::ggplot(ggplot2::aes(x = reorder(a, score), y = score, fill = a)) +
ggplot2::geom_col() +
ggplot2::geom_text(
ggplot2::aes(
label = format(round(score, 2), decimal.mark = ",", )
),
position = ggplot2::position_stack(vjust = .5),
color = "white",
fontface = "bold",
size = 5
) +
ggplot2::scale_y_continuous(labels = scales::number_format(decimal.mark = ",", accuracy = 0.01)) +
ggplot2::scale_fill_manual(NULL, values = c("#b22200", "#282f6b", "#224f20")) +
ggplot2::theme_light() +
ggplot2::labs(
title = "Caso Decotelli: houve plágio?",
subtitle = "Coeficiente de similaridade com dissertação de mestrado de Decotelli (2008)",
y = "Índice de Jaccard",
x = NULL,
caption = "<span style = 'font-size:10pt'>**Dados** dos textos originais disponíveis em *Decotelli (2008)*: bit.ly/3pk8oOe*, Rosa & Coser (2003)*: bit.ly/3pqGQa4, *Machado-da-Silva et al. (1999)*: bit.ly/2Xxsjh2 e *Relatório CVM (2007)*: bit.ly/3aW5Kpz<br>**Elaboração**: analisemacro.com.br</span>"
) +
ggplot2::theme(
legend.position = "none",
axis.text = ggtext::element_markdown(size = 12, face = "bold"),
axis.title = ggtext::element_markdown(size = 12, face = "bold"),
plot.title = ggtext::element_markdown(size = 30, face = "bold", colour = "#282f6b"),
plot.subtitle = ggtext::element_markdown(size = 16),
plot.caption = ggtext::element_textbox_simple(
size = 12,
lineheight = 1,
margin = ggplot2::margin(10, 5.5, 10, 5.5)
)
)

 

 

O que achou? O pacote textreuse é bastante intuitivo e de fácil uso, de modo que até quem não tem conhecimento aprofundado de estatística, NLP, text mining, etc. consegue fazer análises simples como essa. Espero que o exercício tenha ajudado!

Referências

Recomenda-se consultar a documentação dos pacotes utilizados para entendimento aprofundado sobre as funcionalidades.

  • Jure Leskovec, Anand Rajaraman, and Jeff Ullman, Mining of Massive Datasets (Cambridge University Press, 2011).

 

Compartilhe esse artigo

Facebook
Twitter
LinkedIn
WhatsApp
Telegram
Email
Print

Comente o que achou desse artigo

Outros artigos relacionados

Análise exploratória para modelagem preditiva no Python

Antes de desenvolver bons modelos preditivos é necessário organizar e conhecer muito bem os dados. Neste artigo, damos algumas dicas de recursos, como gráficos, análises e estatísticas, que podem ser usados para melhorar o entendimento sobre os dados usando Python.

Como usar modelos do Sklearn para previsão? Uma introdução ao Skforecast

Prever séries temporais é uma tarefa frequente em diversas áreas, porém exige conhecimento e ferramentas específicas. Os modelos de machine learning do Sklearn são populadores, porém são difíceis de aplicar em estruturas temporais de dados. Neste sentido, introduzimos a biblioteca Skforecast, que integra os modelos do Sklearn e a previsão de séries temporais de forma simples.

Boletim AM

Receba diretamente em seu e-mail gratuitamente nossas promoções especiais e conteúdos exclusivos sobre Análise de Dados!

Boletim AM

Receba diretamente em seu e-mail gratuitamente nossas promoções especiais e conteúdos exclusivos sobre Análise de Dados!

como podemos ajudar?

Preencha os seus dados abaixo e fale conosco no WhatsApp

Boletim AM

Preencha o formulário abaixo para receber nossos boletins semanais diretamente em seu e-mail.