.. _sec_naive_bayes: Naive Bayes =========== Ao longo das seções anteriores, aprendemos sobre a teoria da probabilidade e variáveis aleatórias. Para colocar essa teoria em prática, vamos apresentar o classificador *Naive Bayes*. Isso usa apenas fundamentos probabilísticos para nos permitir realizar a classificação dos dígitos. Aprender é fazer suposições. Se quisermos classificar um novo exemplo de dados que nunca vimos antes, temos que fazer algumas suposições sobre quais exemplos de dados são semelhantes entre si. O classificador *Naive Bayes*, um algoritmo popular e notavelmente claro, assume que todos os recursos são independentes uns dos outros para simplificar o cálculo. Nesta seção, vamos aplicar este modelo para reconhecer personagens em imagens. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python %matplotlib inline import math from mxnet import gluon, np, npx from d2l import mxnet as d2l npx.set_np() d2l.use_svg_display() .. raw:: html
.. raw:: html
.. code:: python %matplotlib inline import math import torch import torchvision from d2l import torch as d2l d2l.use_svg_display() .. raw:: html
.. raw:: html
.. code:: python %matplotlib inline import math import tensorflow as tf from d2l import tensorflow as d2l d2l.use_svg_display() .. raw:: html
.. raw:: html
Reconhecimento Ótico de Caracteres ---------------------------------- MNIST :cite:`LeCun.Bottou.Bengio.ea.1998` é um dos conjuntos de dados amplamente usados. Ele contém 60.000 imagens para treinamento e 10.000 imagens para validação. Cada imagem contém um dígito escrito à mão de 0 a 9. A tarefa é classificar cada imagem no dígito correspondente. Gluon fornece uma classe ``MNIST`` no módulo ``data.vision`` para recupera automaticamente o conjunto de dados da Internet. Posteriormente, o Gluon usará a cópia local já baixada. Especificamos se estamos solicitando o conjunto de treinamento ou o conjunto de teste definindo o valor do parâmetro ``train`` para ``True`` ou ``False``, respectivamente. Cada imagem é uma imagem em tons de cinza com largura e altura de :math:`28` com forma (:math:`28`,\ :math:`28`,\ :math:`1`). Usamos uma transformação personalizada para remover a última dimensão do canal. Além disso, o conjunto de dados representa cada pixel por um inteiro não assinado de :math:`8` bits. Nós os quantificamos em recursos binários para simplificar o problema. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python def transform(data, label): return np.floor(data.astype('float32') / 128).squeeze(axis=-1), label mnist_train = gluon.data.vision.MNIST(train=True, transform=transform) mnist_test = gluon.data.vision.MNIST(train=False, transform=transform) .. raw:: html
.. raw:: html
.. code:: python data_transform = torchvision.transforms.Compose( [torchvision.transforms.ToTensor()]) mnist_train = torchvision.datasets.MNIST( root='./temp', train=True, transform=data_transform, download=True) mnist_test = torchvision.datasets.MNIST( root='./temp', train=False, transform=data_transform, download=True) .. parsed-literal:: :class: output 0.4%Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to ./temp/MNIST/raw/train-images-idx3-ubyte.gz 77.4% .. raw:: html
.. raw:: html
.. code:: python ((train_images, train_labels), ( test_images, test_labels)) = tf.keras.datasets.mnist.load_data() .. raw:: html
.. raw:: html
Podemos acessar um exemplo particular, que contém a imagem e o rótulo correspondente. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python image, label = mnist_train[2] image.shape, label .. parsed-literal:: :class: output ((28, 28), array(4, dtype=int32)) .. raw:: html
.. raw:: html
.. code:: python image, label = mnist_train[2] image.shape, label .. parsed-literal:: :class: output (torch.Size([1, 28, 28]), 4) .. raw:: html
.. raw:: html
.. code:: python image, label = train_images[2], train_labels[2] image.shape, label .. parsed-literal:: :class: output ((28, 28), 4) .. raw:: html
.. raw:: html
Nosso exemplo, armazenado aqui na variável ``imagem``, corresponde a uma imagem com altura e largura de :math:`28` pixels. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python image.shape, image.dtype .. parsed-literal:: :class: output ((28, 28), dtype('float32')) .. raw:: html
.. raw:: html
.. code:: python image.shape, image.dtype .. parsed-literal:: :class: output (torch.Size([1, 28, 28]), torch.float32) .. raw:: html
.. raw:: html
.. code:: python image.shape, image.dtype .. parsed-literal:: :class: output ((28, 28), dtype('uint8')) .. raw:: html
.. raw:: html
Nosso código armazena o rótulo de cada imagem como um escalar. Seu tipo é um número inteiro de :math:`32` bits. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python label, type(label), label.dtype .. parsed-literal:: :class: output (array(4, dtype=int32), mxnet.numpy.ndarray, dtype('int32')) .. raw:: html
.. raw:: html
.. code:: python label, type(label) .. parsed-literal:: :class: output (4, int) .. raw:: html
.. raw:: html
.. code:: python label, type(label) .. parsed-literal:: :class: output (4, numpy.uint8) .. raw:: html
.. raw:: html
Também podemos acessar vários exemplos ao mesmo tempo. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python images, labels = mnist_train[10:38] images.shape, labels.shape .. parsed-literal:: :class: output ((28, 28, 28), (28,)) .. raw:: html
.. raw:: html
.. code:: python images = torch.stack([mnist_train[i][0] for i in range(10,38)], dim=1).squeeze(0) labels = torch.tensor([mnist_train[i][1] for i in range(10,38)]) images.shape, labels.shape .. parsed-literal:: :class: output (torch.Size([28, 28, 28]), torch.Size([28])) .. raw:: html
.. raw:: html
.. code:: python images = tf.stack([train_images[i] for i in range(10, 38)], axis=0) labels = tf.constant([train_labels[i] for i in range(10, 38)]) images.shape, labels.shape .. parsed-literal:: :class: output (TensorShape([28, 28, 28]), TensorShape([28])) .. raw:: html
.. raw:: html
Vamos visualizar esses exemplos. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python d2l.show_images(images, 2, 9); .. figure:: output_naive-bayes_6e475d_75_0.svg .. raw:: html
.. raw:: html
.. code:: python d2l.show_images(images, 2, 9); .. figure:: output_naive-bayes_6e475d_78_0.svg .. raw:: html
.. raw:: html
.. code:: python d2l.show_images(images, 2, 9); .. figure:: output_naive-bayes_6e475d_81_0.svg .. raw:: html
.. raw:: html
O Modelo Probabilístico para Classificação ------------------------------------------ Em uma tarefa de classificação, mapeamos um exemplo em uma categoria. Aqui, um exemplo é uma imagem em tons de cinza :math:`28\times 28`, e uma categoria é um dígito. (Consulte :numref:`sec_softmax` para uma explicação mais detalhada.) Uma maneira natural de expressar a tarefa de classificação é por meio da questão probabilística: qual é o rótulo mais provável dado os recursos (ou seja, pixels de imagem)? Denote por :math:`\mathbf x\in\mathbb R^d` as características do exemplo e :math:`y\in\mathbb R` o rótulo. Aqui, os recursos são pixels de imagem, onde podemos remodelar uma imagem :math:`2`-dimensional para um vetor de modo que :math:`d=28^2=784`, e os rótulos são dígitos. A probabilidade do rótulo dado as características é :math:`p(y \mid \mathbf{x})`. Se pudermos calcular essas probabilidades, que são :math:`p(y \mid \mathbf{x})` para :math:`y=0, \ldots,9` em nosso exemplo, então o classificador produzirá a previsão :math:`\hat{y}` dado pela expressão: .. math:: \hat{y} = \mathrm{argmax} \> p(y \mid \mathbf{x}). Infelizmente, isso requer que estimemos :math:`p(y \mid \mathbf{x})` para cada valor de :math:`\mathbf{x} = x_1, ..., x_d`. Imagine que cada recurso pudesse assumir um dos :math:`2` valores. Por exemplo, o recurso :math:`x_1 = 1` pode significar que a palavra maçã aparece em um determinado documento e :math:`x_1 = 0` pode significar que não. Se tivéssemos :math:`30` de tais características binárias, isso significaria que precisamos estar preparados para classificar qualquer um dos :math:`2^{30}` (mais de 1 bilhão!) de valores possíveis do vetor de entrada :math:`\mathbf{x}`. Além disso, onde está o aprendizado? Se precisarmos ver todos os exemplos possíveis para prever o rótulo correspondente, não estaremos realmente aprendendo um padrão, mas apenas memorizando o conjunto de dados. O Classificador Naive Bayes --------------------------- Felizmente, ao fazer algumas suposições sobre a independência condicional, podemos introduzir algum viés indutivo e construir um modelo capaz de generalizar a partir de uma seleção comparativamente modesta de exemplos de treinamento. Para começar, vamos usar o teorema de Bayes, para expressar o classificador como .. math:: \hat{y} = \mathrm{argmax}_y \> p(y \mid \mathbf{x}) = \mathrm{argmax}_y \> \frac{p( \mathbf{x} \mid y) p(y)}{p(\mathbf{x})}. Observe que o denominador é o termo de normalização :math:`p(\mathbf{x})` que não depende do valor do rótulo :math:`y`. Como resultado, só precisamos nos preocupar em comparar o numerador em diferentes valores de :math:`y`. Mesmo que o cálculo do denominador fosse intratável, poderíamos escapar ignorando-o, desde que pudéssemos avaliar o numerador. Felizmente, mesmo se quiséssemos recuperar a constante de normalização, poderíamos. Sempre podemos recuperar o termo de normalização, pois :math:`\sum_y p(y \mid \mathbf{x}) = 1`. Agora, vamos nos concentrar em :math:`p( \mathbf{x} \mid y)`. Usando a regra da cadeia de probabilidade, podemos expressar o termo :math:`p( \mathbf{x} \mid y)` como .. math:: p(x_1 \mid y) \cdot p(x_2 \mid x_1, y) \cdot ... \cdot p( x_d \mid x_1, ..., x_{d-1}, y). Por si só, essa expressão não nos leva mais longe. Ainda devemos estimar cerca de :math:`2^d` parâmetros. No entanto, se assumirmos que *as características são condicionalmente independentes umas das outras, dado o rótulo*, então de repente estamos em uma forma muito melhor, pois este termo simplifica para :math:`\prod_i p(x_i \mid y)`, dando-nos o preditor .. math:: \hat{y} = \mathrm{argmax}_y \> \prod_{i=1}^d p(x_i \mid y) p(y). Se pudermos estimar :math:`p(x_i=1 \mid y)` para cada :math:`i` e :math:`y`, e salvar seu valor em :math:`P_{xy}[i, y]`, aqui :math:`P_{xy}` é uma matriz :math:`d\times n` com :math:`n` sendo o número de classes e :math:`y\in\{1, \ldots, n\}`, então também podemos usar isso para estimar :math:`p(x_i = 0 \mid y)`, ou seja, .. math:: p(x_i = t_i \mid y) = \begin{cases} P_{xy}[i, y] & \text{for } t_i=1 ;\\ 1 - P_{xy}[i, y] & \text{for } t_i = 0 . \end{cases} Além disso, estimamos :math:`p(y)` para cada :math:`y` e o salvamos em :math:`P_y[y]`, com :math:`P_y` um vetor de comprimento :math:`n`. Então, para qualquer novo exemplo :math:`\mathbf t = (t_1, t_2, \ldots, t_d)`, poderíamos calcular .. math:: \begin{aligned}\hat{y} &= \mathrm{argmax}_ y \ p(y)\prod_{i=1}^d p(x_t = t_i \mid y) \\ &= \mathrm{argmax}_y \ P_y[y]\prod_{i=1}^d \ P_{xy}[i, y]^{t_i}\, \left(1 - P_{xy}[i, y]\right)^{1-t_i}\end{aligned} :label: eq_naive_bayes_estimation para qualquer :math:`y`. Portanto, nossa suposição de independência condicional levou a complexidade do nosso modelo de uma dependência exponencial no número de características :math:`\mathcal{O}(2^dn)` para uma dependência linear, que é :math:`\mathcal{O}(dn)`. Trainamento ----------- O problema agora é que não conhecemos :math:`P_{xy}` e :math:`P_y`. Portanto, precisamos primeiro estimar seus valores dados alguns dados de treinamento. Isso é *treinar* o modelo. Estimar :math:`P_y` não é muito difícil. Como estamos lidando apenas com classes de :math:`10`, podemos contar o número de ocorrências :math:`n_y` para cada um dos dígitos e dividi-lo pela quantidade total de dados :math:`n`. Por exemplo, se o dígito 8 ocorre :math:`n_8 = 5,800` vezes e temos um total de :math:`n = 60,000` imagens, a estimativa de probabilidade é :math:`p(y=8) = 0.0967`. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python X, Y = mnist_train[:] # All training examples n_y = np.zeros((10)) for y in range(10): n_y[y] = (Y == y).sum() P_y = n_y / n_y.sum() P_y .. parsed-literal:: :class: output array([0.09871667, 0.11236667, 0.0993 , 0.10218333, 0.09736667, 0.09035 , 0.09863333, 0.10441667, 0.09751666, 0.09915 ]) .. raw:: html
.. raw:: html
.. code:: python X = torch.stack([mnist_train[i][0] for i in range(len(mnist_train))], dim=1).squeeze(0) Y = torch.tensor([mnist_train[i][1] for i in range(len(mnist_train))]) n_y = torch.zeros(10) for y in range(10): n_y[y] = (Y == y).sum() P_y = n_y / n_y.sum() P_y .. parsed-literal:: :class: output tensor([0.0987, 0.1124, 0.0993, 0.1022, 0.0974, 0.0904, 0.0986, 0.1044, 0.0975, 0.0992]) .. raw:: html
.. raw:: html
.. code:: python X = tf.stack([train_images[i] for i in range(len(train_images))], axis=0) Y = tf.constant([train_labels[i] for i in range(len(train_labels))]) n_y = tf.Variable(tf.zeros(10)) for y in range(10): n_y[y].assign(tf.reduce_sum(tf.cast(Y == y, tf.float32))) P_y = n_y / tf.reduce_sum(n_y) P_y .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Agora vamos para coisas um pouco mais difíceis :math:`P_{xy}`. Como escolhemos imagens em preto e branco, :math:`p(x_i \mid y)` denota a probabilidade de que o pixel :math:`i` seja ativado para a classe :math:`y`. Assim como antes, podemos ir e contar o número de vezes :math:`n_{iy}` para que um evento ocorra e dividi-lo pelo número total de ocorrências de :math:`y`, ou seja, :math:`n_y`. Mas há algo um pouco preocupante: certos pixels podem nunca ser pretos (por exemplo, para imagens bem cortadas, os pixels dos cantos podem sempre ser brancos). Uma maneira conveniente para os estatísticos lidarem com esse problema é adicionar pseudo contagens a todas as ocorrências. Portanto, em vez de :math:`n_{iy}`, usamos :math:`n_{y} + 1` e em vez de :math:`n_y` usamos :math:`n_{iy}+1`. Isso também é chamado de *Suavização de Laplace*. Pode parecer ad-hoc, mas pode ser bem motivado do ponto de vista bayesiano. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python n_x = np.zeros((10, 28, 28)) for y in range(10): n_x[y] = np.array(X.asnumpy()[Y.asnumpy() == y].sum(axis=0)) P_xy = (n_x + 1) / (n_y + 1).reshape(10, 1, 1) d2l.show_images(P_xy, 2, 5); .. figure:: output_naive-bayes_6e475d_99_0.svg .. raw:: html
.. raw:: html
.. code:: python n_x = torch.zeros((10, 28, 28)) for y in range(10): n_x[y] = torch.tensor(X.numpy()[Y.numpy() == y].sum(axis=0)) P_xy = (n_x + 1) / (n_y + 1).reshape(10, 1, 1) d2l.show_images(P_xy, 2, 5); .. figure:: output_naive-bayes_6e475d_102_0.svg .. raw:: html
.. raw:: html
.. code:: python n_x = tf.Variable(tf.zeros((10, 28, 28))) for y in range(10): n_x[y].assign(tf.cast(tf.reduce_sum( X.numpy()[Y.numpy() == y], axis=0), tf.float32)) P_xy = (n_x + 1) / tf.reshape((n_y + 1), (10, 1, 1)) d2l.show_images(P_xy, 2, 5); .. figure:: output_naive-bayes_6e475d_105_0.svg .. raw:: html
.. raw:: html
Visualizando essas probabilidades de :math:`10\times 28\times 28` (para cada pixel de cada classe), poderíamos obter alguns dígitos de aparência média. Agora podemos usar :eq:`eq_naive_bayes_estimation` para prever uma nova imagem. Dado :math:`\mathbf x`, as seguintes funções calculam :math:`p(\mathbf x \mid y)p(y)` para cada :math:`y`. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python def bayes_pred(x): x = np.expand_dims(x, axis=0) # (28, 28) -> (1, 28, 28) p_xy = P_xy * x + (1 - P_xy)*(1 - x) p_xy = p_xy.reshape(10, -1).prod(axis=1) # p(x|y) return np.array(p_xy) * P_y image, label = mnist_test[0] bayes_pred(image) .. parsed-literal:: :class: output array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) .. raw:: html
.. raw:: html
.. code:: python def bayes_pred(x): x = x.unsqueeze(0) # (28, 28) -> (1, 28, 28) p_xy = P_xy * x + (1 - P_xy)*(1 - x) p_xy = p_xy.reshape(10, -1).prod(dim=1) # p(x|y) return p_xy * P_y image, label = mnist_test[0] bayes_pred(image) .. parsed-literal:: :class: output tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) .. raw:: html
.. raw:: html
.. code:: python def bayes_pred(x): x = tf.expand_dims(x, axis=0) # (28, 28) -> (1, 28, 28) p_xy = P_xy * x + (1 - P_xy)*(1 - x) p_xy = tf.math.reduce_prod(tf.reshape(p_xy, (10, -1)), axis=1) # p(x|y) return p_xy * P_y image, label = tf.cast(train_images[0], tf.float32), train_labels[0] bayes_pred(image) .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Isso deu terrivelmente errado! Para descobrir o porquê, vejamos as probabilidades por pixel. Normalmente são números entre :math:`0,001` e :math:`1`. Estamos multiplicando :math:`784` deles. Neste ponto, vale a pena mencionar que estamos calculando esses números em um computador, portanto, com um intervalo fixo para o expoente. O que acontece é que experimentamos *underflow numérico*, ou seja, a multiplicação de todos os números pequenos leva a algo ainda menor até que seja arredondado para zero. Discutimos isso como uma questão teórica em :numref:`sec_maximum_likelihood`, mas vemos o fenômeno claramente aqui na prática. Conforme discutido nessa seção, corrigimos isso usando o fato de que :math:`\log a b = \log a + \log b`, ou seja, mudamos para logaritmos de soma. Mesmo se :math:`a` e :math:`b` forem números pequenos, os valores de logaritmo devem estar em uma faixa adequada. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python a = 0.1 print('underflow:', a**784) print('logarithm is normal:', 784*math.log(a)) .. parsed-literal:: :class: output underflow: 0.0 logarithm is normal: -1805.2267129073316 .. raw:: html
.. raw:: html
.. code:: python a = 0.1 print('underflow:', a**784) print('logarithm is normal:', 784*math.log(a)) .. parsed-literal:: :class: output underflow: 0.0 logarithm is normal: -1805.2267129073316 .. raw:: html
.. raw:: html
.. code:: python a = 0.1 print('underflow:', a**784) print('logarithm is normal:', 784*tf.math.log(a).numpy()) .. parsed-literal:: :class: output underflow: 0.0 logarithm is normal: -1805.2267379760742 .. raw:: html
.. raw:: html
Como o logaritmo é uma função crescente, podemos reescrever :eq:`eq_naive_bayes_estimation` como .. math:: \hat{y} = \mathrm{argmax}_y \ \log P_y[y] + \sum_{i=1}^d \Big[t_i\log P_{xy}[x_i, y] + (1-t_i) \log (1 - P_{xy}[x_i, y]) \Big]. Podemos implementar a seguinte versão estável: .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python log_P_xy = np.log(P_xy) log_P_xy_neg = np.log(1 - P_xy) log_P_y = np.log(P_y) def bayes_pred_stable(x): x = np.expand_dims(x, axis=0) # (28, 28) -> (1, 28, 28) p_xy = log_P_xy * x + log_P_xy_neg * (1 - x) p_xy = p_xy.reshape(10, -1).sum(axis=1) # p(x|y) return p_xy + log_P_y py = bayes_pred_stable(image) py .. parsed-literal:: :class: output array([-269.00424, -301.73447, -245.21458, -218.8941 , -193.46907, -206.10315, -292.54315, -114.62834, -220.35619, -163.18881]) .. raw:: html
.. raw:: html
.. code:: python log_P_xy = torch.log(P_xy) log_P_xy_neg = torch.log(1 - P_xy) log_P_y = torch.log(P_y) def bayes_pred_stable(x): x = x.unsqueeze(0) # (28, 28) -> (1, 28, 28) p_xy = log_P_xy * x + log_P_xy_neg * (1 - x) p_xy = p_xy.reshape(10, -1).sum(axis=1) # p(x|y) return p_xy + log_P_y py = bayes_pred_stable(image) py .. parsed-literal:: :class: output tensor([-274.1814, -302.0445, -254.1889, -223.6053, -199.4294, -212.9662, -298.5672, -119.8299, -223.9705, -169.0555]) .. raw:: html
.. raw:: html
.. code:: python log_P_xy = tf.math.log(P_xy) # TODO: Look into why this returns infs log_P_xy_neg = tf.math.log(1 - P_xy) log_P_y = tf.math.log(P_y) def bayes_pred_stable(x): x = tf.expand_dims(x, axis=0) # (28, 28) -> (1, 28, 28) p_xy = log_P_xy * x + log_P_xy_neg * (1 - x) p_xy = tf.math.reduce_sum(tf.reshape(p_xy, (10, -1)), axis=1) # p(x|y) return p_xy + log_P_y py = bayes_pred_stable(image) py .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Podemos agora verificar se a previsão está correta. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python # Convert label which is a scalar tensor of int32 dtype # to a Python scalar integer for comparison py.argmax(axis=0) == int(label) .. parsed-literal:: :class: output array(True) .. raw:: html
.. raw:: html
.. code:: python py.argmax(dim=0) == label .. parsed-literal:: :class: output tensor(True) .. raw:: html
.. raw:: html
.. code:: python tf.argmax(py, axis=0) == label .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Se agora prevermos alguns exemplos de validação, podemos ver o classificador Bayes funciona muito bem. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python def predict(X): return [bayes_pred_stable(x).argmax(axis=0).astype(np.int32) for x in X] X, y = mnist_test[:18] preds = predict(X) d2l.show_images(X, 2, 9, titles=[str(d) for d in preds]); .. figure:: output_naive-bayes_6e475d_159_0.svg .. raw:: html
.. raw:: html
.. code:: python def predict(X): return [bayes_pred_stable(x).argmax(dim=0).type(torch.int32).item() for x in X] X = torch.stack([mnist_train[i][0] for i in range(10,38)], dim=1).squeeze(0) y = torch.tensor([mnist_train[i][1] for i in range(10,38)]) preds = predict(X) d2l.show_images(X, 2, 9, titles=[str(d) for d in preds]); .. figure:: output_naive-bayes_6e475d_162_0.svg .. raw:: html
.. raw:: html
.. code:: python def predict(X): return [tf.cast(tf.argmax(bayes_pred_stable(x), axis=0), tf.int32).numpy() for x in X] X = tf.stack( [tf.cast(train_images[i], tf.float32) for i in range(10, 38)], axis=0) y = tf.constant([train_labels[i] for i in range(10, 38)]) preds = predict(X) # TODO: The preds are not correct due to issues with bayes_pred_stable() d2l.show_images(X, 2, 9, titles=[str(d) for d in preds]); .. figure:: output_naive-bayes_6e475d_165_0.svg .. raw:: html
.. raw:: html
Finalmente, vamos calcular a precisão geral do classificador. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python X, y = mnist_test[:] preds = np.array(predict(X), dtype=np.int32) float((preds == y).sum()) / len(y) # Validation accuracy .. parsed-literal:: :class: output 0.8426 .. raw:: html
.. raw:: html
.. code:: python X = torch.stack([mnist_train[i][0] for i in range(len(mnist_test))], dim=1).squeeze(0) y = torch.tensor([mnist_train[i][1] for i in range(len(mnist_test))]) preds = torch.tensor(predict(X), dtype=torch.int32) float((preds == y).sum()) / len(y) # Validation accuracy .. parsed-literal:: :class: output 0.8364 .. raw:: html
.. raw:: html
.. code:: python X = tf.stack([tf.cast(train_images[i], tf.float32) for i in range( len(test_images))], axis=0) y = tf.constant([train_labels[i] for i in range(len(test_images))]) preds = tf.constant(predict(X), dtype=tf.int32) # TODO: The accuracy is not correct due to issues with bayes_pred_stable() tf.reduce_sum(tf.cast(preds == y, tf.float32)) / len(y) # Validation accuracy .. parsed-literal:: :class: output .. raw:: html
.. raw:: html
Redes profundas modernas alcançam taxas de erro de menos de :math:`0,01`. O desempenho relativamente baixo é devido às suposições estatísticas incorretas que fizemos em nosso modelo: presumimos que cada pixel é gerado *independentemente*, dependendo apenas do rótulo. Claramente, não é assim que os humanos escrevem dígitos, e essa suposição errada levou à queda de nosso classificador excessivamente ingênuo (Bayes). Resumo ------ - Usando a regra de Bayes, um classificador pode ser feito assumindo que todas as características observadas são independentes. - Este classificador pode ser treinado em um conjunto de dados contando o número de ocorrências de combinações de rótulos e valores de pixel. - Esse classificador foi o padrão ouro por décadas para tarefas como detecção de spam. Exercícios ---------- 1. Considere o conjunto de dados :math:`[[0,0], [0,1], [1,0], [1,1]]` com rótulos dados pelo XOR dos dois elementos :math:`[0,1,1,0]`. Quais são as probabilidades de um classificador Naive Bayes construído neste conjunto de dados. Classifica com sucesso nossos pontos? Se não, quais premissas são violadas? 2. Suponha que não usamos a suavização de Laplace ao estimar as probabilidades e um exemplo de dados chegou no momento do teste que continha um valor nunca observado no treinamento. Qual seria a saída do modelo? 3. O classificador Naive Bayes é um exemplo específico de uma rede Bayesiana, onde a dependência de variáveis aleatórias é codificada com uma estrutura de grafo. Embora a teoria completa esteja além do escopo desta seção (consulte :cite:`Koller.Friedman.2009` para detalhes completos), explique por que permitir a dependência explícita entre as duas variáveis de entrada no modelo XOR permite a criação de um classificador de sucesso . .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
`Discussões `__ .. raw:: html
.. raw:: html
`Discussões `__ .. raw:: html
.. raw:: html
`Discussões `__ .. raw:: html
.. raw:: html
.. raw:: html