library(knitr) # colocar figuras/imagens na apresentação
library(readxl) # leitura dos dados em excel
library(flextable) # Construir Tabelas
library(officer) # Pacote para utilizar algumas opções extras do pacote flextable
library(DT) # pacote para construir tabelas dinâmicas
Neste presente trabalho será apresentado a definição de classificadores bayesianos e a sua aplicação na classificação de texto, implementação dos classificadores em alguns exemplos no Rstudio/Rmarkdown.
Classificadores Bayesianos são classificadores estatísticos, que podem predizer a probabilidade de ocorrência de cada classe assim como a probabilidade de uma dada amostra pertencer a uma determinada classe. A classificação Bayesiana é baseada no teorema de Bayes.
Estudos comparando algoritmos de classificação apontaram que o classificador Bayesiano “Ingênuo” (Naive Bayes) pode ser comparado em nível de performance com as árvores de decisão e com os classificadores de Redes Neurais .
Uma das características marcantes dos classificadores Bayesianos é a exatidão e velocidade quando aplicados a grandes bases de dados.
O algoritmo “Naive Bayes” é um classificador probabilístico baseado no “Teorema de Bayes”, o qual foi criado por Thomas Bayes (1701 - 1761) para tentar provar a existência de Deus.O Algoritmo de classificação bayesiana usa o Teorema de Bayes para prever a classe de um objeto, supondo que seus atributos são independentes. Apesar de essa suposição nem sempre ser verdadeira, resultados empíricos descritos na literatura mostram que o algoritmo Naïve-Bayes tem bonsresultados e custo computacional baixo.
knitr::include_graphics("Thomas_Bayes.jpg")
Figura 1: Thomas Bayes (1701-1761).
Hoje é utilizado na área de Aprendizado de Máquina (Machine Learning) para categorizar textos com base na frequência das palavras usadas.Por ser muito simples e rápido, possui um desempenho relativamente maior do que outros classificadores. Além disso, o Naive Bayes só precisa de um pequeno número de dados de teste para concluir classificações com uma boa precisão.
Exemplos de Utilização do Classificador:
Classificar objetos descritos por atributos qualitativos(nominais ou ordinais) e quantitativos(discretos ou contínuos);
Classificação de textos;
Pode ser usado para identificar se determinado e-mail é um SPAM ou sobre qual assunto se refere determinado texto;
Análise de sentimento em redes sociais.
Por exemplo, considere o problema de classificar uma pessoa como sendo do sexo feminino ou masculino , com base no seu nome e nas probabilidades que podem ser obtidas a partir dos exemplos previamente classificados apresentados na Tabela 1.
ft <-qflextable(bayes)
std_border = fp_border(color="black", width = 1)
ft <- border_inner_v(ft, border = std_border )
ftab <- set_caption(ft, "Tabela 1: Conjunto de exemplos de pessoas")
ftab
Nome | Sexo |
Anderson | Masculino |
Anderson | Feminino |
Marcos | Masculino |
Anderson | Masculino |
Lia Morita | Feminino |
Maria | Feminino |
Dilma Rouseff | Feminino |
Para decidir o sexo mais provável de uma pessoa chamada Anderson, considerando os dados na Tabela 1, basta observar que:
A probabilidade de ocorrência do nome Anderson é P(nome = Anderson) = \(\frac{3}{7}\) ;
A probabilidade de Anderson ser do sexo feminino é P(nome = Anderson | sexo = feminino) = \(\frac{1}{4}\) ;
A probabilidade de Anderson ser do sexo masculino é P(nome = Anderson | sexo = masculino ) = \(\frac{2}{3}\);
A probabilidade do sexo feminino no conjunto de exemplos é P(sexo = feminino) = \(\frac{4}{7}\) .
A probabilidade do sexo masculino no conjunto de exemplos é P(sexo = masculino) = \(\frac{3}{7}\) .
Então, de acordo com o Teorema de Bayes, segue que:
P(sexo=feminino| nome=Anderson) = \[\frac{P(nome=Anderson|sexo=feminino)P(sexo=feminino)}{P(nome=Anderson)}\]
= \[\frac{1}{4} * \frac {4}{7}/\frac{3}{7}\] ≅ 33,3%
P(sexo=masculino| nome=Anderson) = \[\frac{P(nome=Anderson|sexo=masculino)P(sexo=masculino)}{P(nome=Anderson)}\]
= \[\frac{2}{3} * \frac {3}{7}/\frac{3}{7}\] ≅ 66,6%
Portanto, pode-se concluir que, de acordo com os dados na Tabela 1, uma pessoa chamada Anderson é mais provavelmente do sexo masculino.
Embora os objetos (pessoas) nesse exemplo sejam descritos por um único atributo (nome) e a classe (sexo) seja binária, o algoritmo Naïve-Bayes também pode ser usado quando os objetos são descritos por vários atributos e há diversas classes. Nesse caso, uma evidência E é dada por um vetor de atributos (cujos elementos E1,E2,…,En são consideradosindependentes) e o Teorema de Bayes pode ser generalizado por
knitr::include_graphics("bayes2.png")
Nos exemplos considerados na Tabela 1, todos os atributos são qualitativos nominais; porém, o algoritmo de classificação bayesiana também pode ser usado com atributos quantitativos contínuos. Neste caso, em vez de representar as probabilidades pela frequência relativa dos valores dos atributos no conjunto de exemplos, o algoritmo representa as probabilidades usando a seguinte função de distribuição de probabilidades normal:
knitr::include_graphics("bayes3.png")
µ = é a média dos valores dos atributos contínuos;
σ = é o desvio padrão correspondente.
Aprendizado de máquina é um subcampo da inteligência artificial que lida com algoritmos de computação que podem ser melhorados via dados de treinamento sem programação explícita. É considerado o caminho mais promissor para alcançar a inteligência artificial verdadeiramente próxima à humana.
Algoritmos de aprendizado de máquina podem ser classificados, de maneira geral, em três categorias:
Aprendizagem supervisionada: São feitas entradas de rótulos e exemplos atuais a respectiva saída desejada e isso permite ao algoritmo aprender as regras que mapeiam entradas e saídas.
Aprendizagem não supervisionada: Os rótulos não são fornecidos e, portanto, o algoritmo pode encontrar sua própria estrutura de processamento das entradas (por exemplo, encontrando padrões ocultos nos dados).
Aprendizagem por reforço: O algoritmo interage repetidamente com um ambiente dinâmico com um objetivo específico como, por exemplo, ganhar um jogo ou dirigir um carro. O algoritmo chega à solução mais otimizada para o problema por meio de repetidos erros e tentativas.
O Teorema de Bayes encontra a probabilidade de um evento ocorrer dada a probabilidade de outro evento já ter ocorrido. O teorema de Bayes é afirmado matematicamente da seguinte forma:
knitr::include_graphics("bayes4.png")
Onde:
P(A|B): Probabilidade do evento A acontecer;
P(B|A): Probabilidade de B acontecer, dado que A já ocorreu;
P(A): Probabilidade de A ocorrer;
P(B): Probabilidade de B acontecer.
Adaptado livremente de “Machine Learning with R”, Brett Lanz, Packt.
Os dados foram adaptados de SMS Spam Collection
Este conjunto de dados, sms_spam.xlsx, inclui o texto de mensagens SMS juntamente com um rótulo que indica se a mensagem é indesejada. As mensagens indesejadas são rotuladas como spam, enquanto mensagens legítimas são marcadas como ham.
library(readxl)
sms_raw <- read_excel("sms_spam.xlsx")
str(sms_raw)
## tibble [5,574 x 2] (S3: tbl_df/tbl/data.frame)
## $ type: chr [1:5574] "ham" "ham" "spam" "ham" ...
## $ text: chr [1:5574] "Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat..." "Ok lar... Joking wif u oni..." "Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005. Text FA to 87121 to receive entry question("| __truncated__ "U dun say so early hor... U c already then say..." ...
##
## ham spam
## 87 13
87% são mensagens que foram classificadas como genuína
13% classificadas como SPAM
Utiliza-se o pacote ´tm´ criado por Ingo Feinerer para preparar os dados para o processamento.
library(tm)
## Carregando pacotes exigidos: NLP
##
## Attaching package: 'NLP'
## The following object is masked from 'package:ggplot2':
##
## annotate
## <<SimpleCorpus>>
## Metadata: corpus specific: 1, document level (indexed): 0
## Content: documents: 5574
inspect(sms_corpus[1:10])
## <<SimpleCorpus>>
## Metadata: corpus specific: 1, document level (indexed): 0
## Content: documents: 10
##
## [1] Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat...
## [2] Ok lar... Joking wif u oni...
## [3] Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005. Text FA to 87121 to receive entry question(std txt rate)T&C's apply 08452810075over18's
## [4] U dun say so early hor... U c already then say...
## [5] Nah I don't think he goes to usf, he lives around here though
## [6] FreeMsg Hey there darling it's been 3 week's now and no word back! I'd like some fun you up for it still? Tb ok! XxX std chgs to send, £1.50 to rcv
## [7] Even my brother is not like to speak with me. They treat me like aids patent.
## [8] As per your request 'Melle Melle (Oru Minnaminunginte Nurungu Vettam)' has been set as your callertune for all Callers. Press *9 to copy your friends Callertune
## [9] WINNER!! As a valued network customer you have been selected to receivea £900 prize reward! To claim call 09061701461. Claim code KL341. Valid 12 hours only.
## [10] Had your mobile 11 months or more? U R entitled to Update to the latest colour mobiles with camera for Free! Call The Mobile Update Co FREE on 08002986030
datatable(sms_raw,
extensions = 'Buttons', options = list(
dom = 'Bfrtip',
buttons =
list('copy', 'print', list(
extend = 'collection',
buttons = c('csv', 'excel', 'pdf'),
text = 'Download'
))
)
)
## Warning in tm_map.SimpleCorpus(sms_corpus, tolower): transformation drops
## documents
## Warning in tm_map.SimpleCorpus(corpus_limpo, removeNumbers): transformation
## drops documents
## Warning in tm_map.SimpleCorpus(corpus_limpo, removeWords, stopwords()):
## transformation drops documents
## Warning in tm_map.SimpleCorpus(corpus_limpo, removePunctuation): transformation
## drops documents
## Warning in tm_map.SimpleCorpus(corpus_limpo, stripWhitespace): transformation
## drops documents
sms_raw_treinamento <- sms_raw[1:4169,]
sms_raw_teste <- sms_raw[4170:5574,]
sms_dtm_treinamento <- sms_dtm[1:4169,]
sms_dtm_teste <- sms_dtm[4170:5574,]
sms_corpus_treinamento <- corpus_limpo[1:4169]
sms_corpus_teste <- corpus_limpo[4170:5574]
# treinamento
options(digits = 2)
prop.table(table(sms_raw_treinamento$type)) * 100
##
## ham spam
## 86 14
#teste
prop.table(table(sms_raw_teste$type)) * 100
##
## ham spam
## 87 13
Como pode ser notado, as proporções entre spam e ham são praticamente idênticas nos dois conjuntos de dados.
library(wordcloud)
Uma word cloud (nuvem de palavras) é uma forma bastante comum de descrever visualmente a frequência com que as palavra aparecem em um texto. A nuvem é composta de palavras espalhadas aleatoriamente. As palavras que aparecem com mais frequência no texto são mostradas em uma fonte maior, enquanto termos menos comuns são mostrados em fontes menores. Esse tipo de figura cresceu em popularidade recentemente, uma vez que fornece uma maneira de observar temas de tendências em sítios de mídia social e em discursos.
O pacote wordcloud foi escrito por Ian Fellows. Maiores informações podem ser obtidas aqui.
wordcloud(sms_corpus_treinamento, min.freq = 40, random.order = FALSE)
Figura 2: Nuvem de todos as palavras.
spam <- subset(sms_raw_treinamento, type == "spam")
wordcloud(spam$text, max.words = 40, scale = c(4, 0.5))
## Warning in tm_map.SimpleCorpus(corpus, tm::removePunctuation): transformation
## drops documents
## Warning in tm_map.SimpleCorpus(corpus, function(x) tm::removeWords(x,
## tm::stopwords())): transformation drops documents
Figura 3: Nuvem de palavras do tipo SPAM
ham <- subset(sms_raw_treinamento, type == "ham")
wordcloud(ham$text, max.words = 40, scale = c(4, 0.5))
## Warning in tm_map.SimpleCorpus(corpus, tm::removePunctuation): transformation
## drops documents
## Warning in tm_map.SimpleCorpus(corpus, function(x) tm::removeWords(x,
## tm::stopwords())): transformation drops documents
Figura 4: Nuvem de palavras do tipo HAM.
A etapa final no processo de preparação de dados é transformar a matriz esparsa em uma estrutura de dados que pode ser utilizada para treinar o classificador Naive Bayes. Atualmente, a matriz esparsa inclui mais de 7.000 atributos, um para cada palavra que aparece em pelo menos uma mensagem SMS. É improvável que todos estes sejam úteis para a classificação. Para reduzir o número de atributos (palavras), elimina-se as palavras que aparecem em menos de cinco mensagens SMS, ou seja, menos de cerca de 0,1% dos dados utilzados para o treinamento do modelo.
meus_termos <- findFreqTerms(sms_dtm_treinamento, 5)
sms_treinamento <- DocumentTermMatrix(sms_corpus_treinamento, list(dictionary = meus_termos))
sms_teste <- DocumentTermMatrix(sms_corpus_teste, list(dictionary = meus_termos))
O classificador Naive Bayes normalmente é treinado em dados com atributos categóricos.Devemos mudar isso para um fator que simplesmente indique Sim ou não, dependendo se a palavra aparece ou não no texto.
O código a seguir define uma função converte_contagens() para converter contagens em fatores e aplica a função a cada coluna das matrizes.
O resultado serão duas matrizes, cada uma com colunas de tipo fator indicando Sim se a palavra de cada coluna aparece nas mensagens que compõem as linhas e Não em caso contrário.
converte_contagens <- function(contagem) {
contagem <- ifelse(contagem > 0, 1, 0)
contagem <- factor(contagem, levels = c(0, 1), labels = c("Nao", "Sim"))
return(contagem)
}
sms_treinamento <- apply(sms_treinamento, MARGIN = 2, converte_contagens)
sms_teste <- apply(sms_teste, MARGIN = 2, converte_contagens)
O algoritmo Naive Bayes está implementado em, pelo menos, dois pacotes em R: e1071 e klaR. Pode-se utilizar qualquer um deles. Aqui utiliza-se o primeiro, e1071. O modelo utiliza a presença ou a ausência de palavras para estimar a probabilidade de que uma dada mensagem é spam.
library(e1071)
##
## Attaching package: 'e1071'
## The following object is masked from 'package:raster':
##
## interpolate
sms_classificador <- naiveBayes(sms_treinamento, sms_raw_treinamento$type)
sms_teste_pred <- predict(sms_classificador, sms_teste)
Para comparar os valores previstos com os valores reais, usaremos a função CrossTable() no pacote gmodels.
library(gmodels)
## Registered S3 method overwritten by 'gdata':
## method from
## reorder.factor DescTools
##
## Attaching package: 'gmodels'
## The following object is masked from 'package:descr':
##
## CrossTable
CrossTable(sms_teste_pred, sms_raw_teste$type,
prop.chisq = FALSE, prop.t = FALSE,
dnn = c('predito', 'real'))
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Row Total |
## | N / Col Total |
## |-------------------------|
##
##
## Total Observations in Table: 1405
##
##
## | real
## predito | ham | spam | Row Total |
## -------------|-----------|-----------|-----------|
## ham | 1216 | 25 | 1241 |
## | 0.980 | 0.020 | 0.883 |
## | 0.995 | 0.137 | |
## -------------|-----------|-----------|-----------|
## spam | 6 | 158 | 164 |
## | 0.037 | 0.963 | 0.117 |
## | 0.005 | 0.863 | |
## -------------|-----------|-----------|-----------|
## Column Total | 1222 | 183 | 1405 |
## | 0.870 | 0.130 | |
## -------------|-----------|-----------|-----------|
##
##
Olhando para a tabela, nota-se que 6 de 1216 mensagens ham (0,037%) foram classificadas incorretamente como spam, enquanto 25 de 158 mensagens spam (15,8%) foram classificadas incorretamente como ham. Um bom resultado para um classificador tão simples.
Uma das mais famosas, é a biblioteca Scikit Learn (ou sklearn).
O Sklearn implementa o Naive Bayes de 3 formas:
Gaussian;
Multinomial;
Bernoulli;
No Github podemos encontrar a implementação do Naive Bayes em diversas linguagens, incluindo Ruby.
A gem “nbayes” implementa de uma forma muito simples de usar para classificar texto.