Gaussian Naive Bayes, explicado

Gaussian Naive Bayes, explicado

Nó Fonte: 2021431

Gaussian Naive Bayes, explicado
A região de decisão de um classificador Gaussiano Naive Bayes. Imagem do Autor.

 

Acho que esse é um clássico no início de cada carreira em ciência de dados: o Classificador Naive Bayes. Ou eu deveria dizer o família de classificadores ingênuos de Bayes, pois eles vêm em muitos sabores. Por exemplo, existe um Bayes ingênuo multinomial, um Bayes ingênuo de Bernoulli e também um classificador Bayes ingênuo Gaussiano, cada um diferente em apenas um pequeno detalhe, como veremos. Os algoritmos ingênuos de Bayes são bastante simples em design, mas provaram ser úteis em muitas situações complexas do mundo real.

Neste artigo, você pode aprender

  • como funcionam os classificadores ingênuos de Bayes,
  • por que faz sentido defini-los do jeito que são e
  • como implementá-los em Python usando NumPy.

Você pode encontrar o código em meu Github.

Pode ajudar um pouco verificar minha cartilha sobre estatísticas bayesianas Uma introdução suave à inferência bayesiana para se acostumar com a fórmula de Bayes. Como iremos implementar o classificador de forma scikit para aprender, também vale a pena conferir meu artigo Crie sua própria regressão scikit-learn personalizada. No entanto, a sobrecarga do scikit-learn é muito pequena e você deve conseguir acompanhar de qualquer maneira.

Começaremos explorando a teoria surpreendentemente simples da classificação ingênua de Bayes e depois passaremos para a implementação.

Em que estamos realmente interessados ​​ao classificar? O que estamos realmente fazendo, qual é a entrada e a saída? A resposta é simples:

Dado um ponto de dados x, qual é a probabilidade de x pertencer a alguma classe c?

Isso é tudo o que queremos responder com qualquer classificação. Você pode modelar diretamente esta declaração como uma probabilidade condicional: p(c|x).

Por exemplo, se houver

  • aulas 3 c₁c₂c₃ e
  • consiste em 2 recursos x₁x₂,

o resultado de um classificador pode ser algo como p(c₁|x₁x₂) = 0.3, p(c₂|x₁x₂)=0.5 e p(c₃|x₁x₂) = 0.2. Se nos importarmos com um único rótulo como saída, escolheríamos aquele com a maior probabilidade, ou seja, c₂ com uma probabilidade de 50% aqui.

O classificador ingênuo de Bayes tenta calcular essas probabilidades diretamente.

Baías ingénuas

Ok, dado um ponto de dados x, queremos calcular p(c|x) para todas as classes e, em seguida, emitir o c com a maior probabilidade. Em fórmulas, você costuma ver isso como

 

Gaussian Naive Bayes, explicado
Imagem do Autor.

 

Observação: max p(c|x) retorna a probabilidade máxima enquanto argmax p(c|x) retorna o c com esta maior probabilidade.

Mas antes que possamos otimizar p(c|x), temos que ser capazes de calculá-lo. Para isso, utilizamos Teorema de Bayes:

 

Gaussian Naive Bayes, explicado
Teorema de Bayes. Imagem do Autor.

 

Esta é a parte de Bayes do ingênuo Bayes. Mas agora, temos o seguinte problema: Quais são p(x|c) e p(c)?

É disso que se trata o treinamento de um classificador ingênuo de Bayes.

O treinamento

Para ilustrar tudo, vamos usar um conjunto de dados de brinquedo com duas características reais x₁x₂três classes c₁c₂c₃ no seguinte.

 

Gaussian Naive Bayes, explicado
Os dados, visualizados. Imagem do Autor.

 

Você pode criar este conjunto de dados exato via

from sklearn.datasets import make_blobs X, y = make_blobs(n_samples=20, centers=[(0,0), (5,5), (-5, 5)], random_state=0)

Vamos começar com o probabilidade de classe p(c), a probabilidade de que alguma classe c é observado no conjunto de dados rotulado. A maneira mais simples de estimar isso é apenas calcular as frequências relativas das classes e usá-las como probabilidades. Podemos usar nosso conjunto de dados para ver o que isso significa exatamente.

Há 7 de 20 pontos rotulados como classe c₁ (azul) no conjunto de dados, portanto dizemos p(c₁)=7/20. Temos 7 pontos para a classe c₂ (vermelho) também, portanto, definimos p(c₂)=7/20. a última aula c₃ (amarelo) tem apenas 6 pontos, portanto p(c₃) = 6/20.

Esse cálculo simples das probabilidades de classe se assemelha a uma abordagem de máxima verossimilhança. Você pode, no entanto, também usar outro prévio distribuição, se quiser. Por exemplo, se você sabe que este conjunto de dados não é representativo da população real porque a classe c₃ deve aparecer em 50% dos casos, então você define p(c₁) = 0.25, p(c₂)=0.25 e p(c₃) = 0.5. O que quer que ajude você a melhorar o desempenho no conjunto de teste.

Agora nos voltamos para o probabilidade p(x|c)=p(x₁x₂|c). Uma abordagem para calcular essa probabilidade é filtrar o conjunto de dados para amostras com rótulo e, em seguida, tente encontrar uma distribuição (por exemplo, um gaussiano bidimensional) que capture os recursos x₁x₂.

Infelizmente, geralmente não temos amostras suficientes por classe para fazer uma estimativa adequada da probabilidade.

Para poder construir um modelo mais robusto, fazemos o suposição ingênua que as características x₁x₂ e guarante que os mesmos estão estocasticamente independente, dado c. Esta é apenas uma maneira elegante de tornar a matemática mais fácil por meio de

 

Gaussian Naive Bayes, explicado
Imagem do autor

 

para cada aula c. É aqui que o ingénuo parte do ingênuo Bayes vem porque esta equação não se sustenta em geral. Ainda assim, mesmo assim, o ingênuo Bayes produz resultados bons, às vezes excelentes, na prática. Especialmente para problemas de PNL com recursos de bag-of-words, o multinomial ingênuo Bayes brilha.

Os argumentos fornecidos acima são os mesmos para qualquer classificador ingênuo de Bayes que você possa encontrar. Agora só depende de como você modela p(x₁|c₁), p(x₂|c₁), p(x₁|c₂), p(x₂|c₂), p(x₁|c₃)p(x₂|c₃).

Se seus recursos são 0 e 1 apenas, você pode usar um Distribuição Bernoulli. Se forem números inteiros, um Distribuição multinomial. No entanto, temos valores reais de recursos e decidimos por um Gaussian distribuição, daí o nome Gaussian Naive Bayes. Assumimos a seguinte forma

 

Gaussian Naive Bayes, explicado
Imagem do Autor.

 

onde μᵢ,ⱼ é a média e σᵢ,ⱼ é o desvio padrão que temos que estimar a partir dos dados. Isso significa que obtemos uma média para cada recurso i juntamente com uma classe cⱼ, no nosso caso 2*3=6 significa. O mesmo vale para os desvios padrão. Isso pede um exemplo.

Vamos tentar estimar μ₂,₁σ₂,₁. Porque j=1, estamos interessados ​​apenas na classe c₁, vamos manter apenas amostras com este rótulo. Restam as seguintes amostras:

# samples with label = c_1 array([[ 0.14404357, 1.45427351], [ 0.97873798, 2.2408932 ], [ 1.86755799, -0.97727788], [ 1.76405235, 0.40015721], [ 0.76103773, 0.12167502], [-0.10321885, 0.4105985 ], [ 0.95008842, -0.15135721]])

Agora, por causa i=2 temos que considerar apenas a segunda coluna. µ₂,₁ é a média e σ₂,₁ o desvio padrão para esta coluna, ou seja µ₂,₁ = 0.49985176 e σ₂,₁ = 0.9789976.

Esses números fazem sentido se você olhar o gráfico de dispersão de cima novamente. As características x₂ das amostras da classe c₁ estão em torno de 0.5, como você pode ver na foto.

Calculamos isso agora para as outras cinco combinações e terminamos!

Em Python, você pode fazer assim:

from sklearn.datasets import make_blobs
import numpy as np # Create the data. The classes are c_1=0, c_2=1 and c_3=2.
X, y = make_blobs( n_samples=20, centers=[(0, 0), (5, 5), (-5, 5)], random_state=0
) # The class probabilities.
# np.bincounts counts the occurence of each label.
prior = np.bincount(y) / len(y) # np.where(y==i) returns all indices where the y==i.
# This is the filtering step.
means = np.array([X[np.where(y == i)].mean(axis=0) for i in range(3)])
stds = np.array([X[np.where(y == i)].std(axis=0) for i in range(3)])

Recebemos

# priors
array([0.35, 0.35, 0.3 ])
# means array([[ 0.90889988, 0.49985176], [ 5.4111385 , 4.6491892 ], [-4.7841679 , 5.15385848]])
# stds
array([[0.6853714 , 0.9789976 ], [1.40218915, 0.67078568], [0.88192625, 1.12879666]])

Este é o resultado do treinamento de um classificador Gaussiano Naive Bayes.

Fazendo previsões

A fórmula de previsão completa é

 

Gaussian Naive Bayes, explicado
Imagem do Autor.

 

Vamos supor um novo ponto de dados x*=(-2, 5) entra.

 

Gaussian Naive Bayes, explicado
Imagem do Autor.

 

Para ver a qual classe ele pertence, vamos calcular p(c|x*) para todas as classes. Pela foto, deve pertencer à classe c₃ = 2, mas vamos ver. Ignoremos o denominador p(x) por um segundo. Usando o loop a seguir, calculou os nominadores para j = 1, 2, 3.

x_new = np.array([-2, 5]) for j in range(3): print( f"Probability for class {j}: {(1/np.sqrt(2*np.pi*stds[j]**2)*np.exp(-0.5*((x_new-means[j])/stds[j])**2)).prod()*p[j]:.12f}" )

Recebemos

Probability for class 0: 0.000000000263
Probability for class 1: 0.000000044359
Probability for class 2: 0.000325643718

Claro, estes probabilidades (não devemos chamá-los assim) não somam um, pois ignoramos o denominador. No entanto, isso não é problema, pois podemos simplesmente pegar essas probabilidades não normalizadas e dividi-las por sua soma, então elas somarão um. Assim, dividindo esses três valores pela soma de cerca de 0.00032569, obtemos

 

Gaussian Naive Bayes, explicado
Imagem do Autor.

 

Um vencedor claro, como esperávamos. Agora, vamos implementá-lo!

Esta implementação não é de longe eficiente, não é numericamente estável, apenas serve a um propósito educacional. Discutimos a maioria das coisas, então deve ser fácil acompanhar agora. Você pode ignorar todos os check funções ou leia meu artigo Crie seu próprio scikit-learn personalizado se você estiver interessado no que eles fazem exatamente.

Apenas observe que eu implementei um predict_proba método primeiro para calcular probabilidades. O método predict apenas chama esse método e retorna o índice (=classe) com a maior probabilidade usando uma função argmax (aí está de novo!). A classe aguarda classes de 0 a k-1, onde k é o número de aulas.

import numpy as np
from sklearn.base import BaseEstimator, ClassifierMixin
from sklearn.utils.validation import check_X_y, check_array, check_is_fitted class GaussianNaiveBayesClassifier(BaseEstimator, ClassifierMixin): def fit(self, X, y): X, y = check_X_y(X, y) self.priors_ = np.bincount(y) / len(y) self.n_classes_ = np.max(y) + 1 self.means_ = np.array( [X[np.where(y == i)].mean(axis=0) for i in range(self.n_classes_)] ) self.stds_ = np.array( [X[np.where(y == i)].std(axis=0) for i in range(self.n_classes_)] ) return self def predict_proba(self, X): check_is_fitted(self) X = check_array(X) res = [] for i in range(len(X)): probas = [] for j in range(self.n_classes_): probas.append( ( 1 / np.sqrt(2 * np.pi * self.stds_[j] ** 2) * np.exp(-0.5 * ((X[i] - self.means_[j]) / self.stds_[j]) ** 2) ).prod() * self.priors_[j] ) probas = np.array(probas) res.append(probas / probas.sum()) return np.array(res) def predict(self, X): check_is_fitted(self) X = check_array(X) res = self.predict_proba(X) return res.argmax(axis=1)

Testando a Implementação

Embora o código seja bastante curto, ainda é muito longo para ter certeza absoluta de que não cometemos nenhum erro. Então, vamos verificar como ele se sai em relação ao classificador GaussianNB scikit-learn.

my_gauss = GaussianNaiveBayesClassifier()
my_gauss.fit(X, y)
my_gauss.predict_proba([[-2, 5], [0,0], [6, -0.3]])

outputs

array([[8.06313823e-07, 1.36201957e-04, 9.99862992e-01], [1.00000000e+00, 4.23258691e-14, 1.92051255e-11], [4.30879705e-01, 5.69120295e-01, 9.66618838e-27]])

As previsões usando o predict método são

# my_gauss.predict([[-2, 5], [0,0], [6, -0.3]])
array([2, 0, 1])

Agora, vamos usar o scikit-learn. Jogando em algum código

from sklearn.naive_bayes import GaussianNB gnb = GaussianNB()
gnb.fit(X, y)
gnb.predict_proba([[-2, 5], [0,0], [6, -0.3]])

rendimentos

array([[8.06314158e-07, 1.36201959e-04, 9.99862992e-01], [1.00000000e+00, 4.23259111e-14, 1.92051343e-11], [4.30879698e-01, 5.69120302e-01, 9.66619630e-27]])

Os números se parecem um pouco com os do nosso classificador, mas estão um pouco errados nos últimos dígitos exibidos. Fizemos algo errado? Não. A versão scikit-learn apenas usa outro hiperparâmetro var_smoothing=1e-09 . Se definirmos este para zero, obtemos exatamente nossos números. Perfeito!

Dê uma olhada nas regiões de decisão do nosso classificador. Também marquei os três pontos que usamos para testar. Aquele ponto próximo à fronteira tem apenas 56.9% de chance de pertencer à classe vermelha, como você pode ver no predict_proba saídas. Os outros dois pontos são classificados com muito mais confiança.

 

Gaussian Naive Bayes, explicado

As regiões de decisão com os 3 novos pontos. Imagem do Autor.

 

Neste artigo, aprendemos como o classificador Gaussiano Naive Bayes funciona e demos uma intuição sobre por que ele foi projetado dessa forma - é uma abordagem direta para modelar a probabilidade de interesse. Compare isso com a regressão logística: lá, a probabilidade é modelada usando uma função linear com uma função sigmoide aplicada sobre ela. Ainda é um modelo fácil, mas não parece tão natural quanto um classificador ingênuo de Bayes.

Continuamos calculando alguns exemplos e coletando algumas partes úteis de código no caminho. Por fim, implementamos um classificador Gaussiano Naive Bayes completo de uma maneira que funciona bem com o scikit-learn. Isso significa que você pode usá-lo em pipelines ou pesquisa em grade, por exemplo.

No final, fizemos uma pequena verificação de sanidade importando o próprio classificador Gaussian Naive Bayes do scikit-learns e testando se ambos, nosso classificador e o do scikit-learn, produzem o mesmo resultado. Este teste foi bem-sucedido.

 
 
Dr. Robert Kubler é Cientista de Dados da Publicis Media e Autor da Towards Data Science.

 
Óptimo estado. Original. Republicado com permissão.
 

Carimbo de hora:

Mais de KDnuggetsGenericName