Escolar Documentos
Profissional Documentos
Cultura Documentos
Uma forma de visualizar esse conjunto de pixels é como uma nuvem de pontos em um espaço de cores
tridimensional. Vamos remodelar os dados para [n_samples x n_features] e redimensionar as cores
para que fiquem entre 0 e 1:
Fora[20]: (273280, 3)
Podemos visualizar esses pixels neste espaço de cores, usando um subconjunto de 10.000 pixels para
eficiência (Figura 5-121):
fig.suptitle(título, tamanho=20);
Agora vamos reduzir esses 16 milhões de cores para apenas 16 cores, usando um agrupamento k-
means no espaço de pixels. Como estamos lidando com um conjunto de dados muito grande,
usaremos o minilote k-means, que opera em subconjuntos de dados para calcular o resultado muito
mais rapidamente do que o algoritmo k-means padrão (Figura 5-122) :
plot_pixels(dados, cores=novas_cores,
title="Espaço de cores reduzido: 16 cores")
O resultado é uma recoloração dos pixels originais, onde cada pixel recebe a cor do centro do cluster
mais próximo. Plotar essas novas cores no espaço da imagem, em vez do espaço de pixels, nos
mostra o efeito disso (Figura 5-123):
Em[24]:
china_recolored = new_colors.reshape(china.shape)
Figura 5-123. Uma comparação entre a imagem colorida (esquerda) e a imagem de 16 cores (direita)
Alguns detalhes certamente são perdidos no painel mais à direita, mas a imagem geral ainda é
facilmente reconhecível. Esta imagem à direita atinge um fator de compressão de cerca de 1 milhão!
Embora esta seja uma aplicação interessante do k-means, certamente existem maneiras melhores de
compactar informações em imagens. Mas o exemplo mostra o poder de pensar fora da caixa com
métodos não supervisionados como k-means.
Vamos dar uma olhada em alguns dos pontos fracos do k-means e pensar em como
podemos melhorar o modelo de cluster. Como vimos na seção anterior, dados simples e
bem separados, k-means encontra resultados de agrupamento adequados.
Por exemplo, se tivermos blocos simples de dados, o algoritmo k-means pode rotular rapidamente
esses clusters de uma forma que corresponda ao que poderíamos fazer a olho nu (Figura 5-124):
De um ponto de vista intuitivo, poderíamos esperar que a atribuição de agrupamento para alguns
pontos seja mais certa do que para outros; por exemplo, parece haver uma sobreposição muito
ligeira entre os dois clusters intermediários, de tal forma que podemos não ter total confiança na
atribuição de pontos do cluster entre eles. Infelizmente, o modelo k-means não tem medida intrínseca
de probabilidade ou incerteza de atribuições de cluster
(embora possa ser possível utilizar uma abordagem bootstrap para estimar esta incerteza).
Para isso, devemos pensar em generalizar o modelo.
Uma maneira de pensar sobre o modelo k-means é colocar um círculo (ou, em dimensões
superiores, uma hiperesfera) no centro de cada cluster, com um raio definido pelo ponto
mais distante do cluster. Este raio atua como um ponto de corte para atribuição de cluster
dentro do conjunto de treinamento: qualquer ponto fora deste círculo não é considerado
membro do cluster. Podemos visualizar este modelo de cluster com a seguinte função
(Figura 5-125):
Em
[4]: de sklearn.cluster importar KMeans
de scipy.spatial.distance importar cdist
# plote os dados de
entrada ax = ax ou
plt.gca()
ax.axis('equal') ax.scatter(X[:, 0], X[:, 1], c=labels, s=40, cmap= 'viridis', ordem z = 2)
Uma observação importante para k-means é que esses modelos de cluster devem ser circulares: k-means
não possui uma maneira integrada de contabilizar clusters oblongos ou elípticos. Assim, por exemplo, se
pegarmos os mesmos dados e transformá-los, as atribuições do cluster acabam ficando confusas (Figura
5-126):
A olho nu, reconhecemos que estes aglomerados transformados são não circulares e, portanto, aglomerados
circulares seriam um ajuste inadequado. No entanto, k-means não é flexível o suficiente para dar conta
disso e tenta forçar o ajuste dos dados em quatro clusters circulares. Isto resulta numa mistura de
atribuições de cluster onde os círculos resultantes se sobrepõem: veja especialmente o canto inferior direito
deste gráfico. Poderíamos imaginar abordar esta situação específica pré-processando os dados com PCA
(ver “Em profundidade: análise de componentes principais” na página 433), mas na prática não há garantia
de que tal operação global circularizará os dados individuais.
Essas duas desvantagens do k-means – sua falta de flexibilidade no formato do cluster e falta de atribuição
probabilística de cluster – significam que para muitos conjuntos de dados (especialmente conjuntos de
dados de baixa dimensão) ele pode não funcionar tão bem quanto você poderia esperar.
Você pode imaginar abordar essas fraquezas generalizando o modelo k-means: por exemplo, você poderia
medir a incerteza na atribuição de clusters comparando as distâncias de cada ponto a todos os centros dos
clusters, em vez de focar apenas nos mais próximos.
Você também pode imaginar permitir que os limites do cluster sejam elipses em vez de círculos.
cles, de modo a dar conta de clusters não circulares. Acontece que estes são dois componentes essenciais
componentes de um tipo diferente de modelo de agrupamento, modelos de mistura gaussiana.
[[ 0. 0. 0,475 0,525]
[ 0. 1. 0. 0. ]
[ 0. 1. 0. 0. ]
[ 0. 0. 0. 1. ]
[ 0. 1. 0. 0. ]]
Podemos visualizar esta incerteza, por exemplo, fazendo com que o tamanho de cada ponto pro-
proporcional à certeza de sua previsão; olhando para a Figura 5-128, podemos ver que
são precisamente os pontos nas fronteiras entre os clusters que refletem esta incerteza na atribuição dos clusters:
Figura 5-128. Rótulos probabilísticos do GMM: as probabilidades são mostradas pelo tamanho dos pontos
Nos bastidores, um modelo de mistura gaussiana é muito semelhante ao k-means: ele usa uma abordagem de
maximização de expectativa que qualitativamente faz o seguinte:
até convergir:
a. E-step: para cada ponto, encontre pesos que codifiquem a probabilidade de pertencimento a
cada cluster
b. Etapa M: para cada cluster, atualize sua localização, normalização e forma com base
em todos os pontos de dados, fazendo uso dos pesos
O resultado disso é que cada cluster está associado não a uma esfera de arestas rígidas, mas a um modelo
gaussiano suave. Assim como na abordagem de maximização de expectativa k-médias, esse algoritmo pode às
vezes perder a solução globalmente ótima e, portanto, na prática, são usadas múltiplas inicializações aleatórias.
Vamos criar uma função que nos ajudará a visualizar as localizações e formas dos clusters do GMM desenhando
elipses com base na saída do gmm :
Em [10]:
de matplotlib.patches importar Ellipse
ângulo = 0
largura, altura = 2 * np.sqrt(covariância)
# Desenhe a elipse
para nsig em range(1, 4):
ax.add_patch(Ellipse(position, nsig * width, nsig * height, angle, **kwargs))
Com isso implementado, podemos dar uma olhada no que o GMM de quatro componentes nos fornece
para nossos dados iniciais (Figura 5-129):
Da mesma forma, podemos usar a abordagem GMM para ajustar nosso conjunto de dados ampliado;
permitindo uma covariância completa, o modelo se ajustará até mesmo a clusters muito oblongos e esticados
(Figura 5-130):
Figura 5-130. Representação do GMM de quatro componentes na presença de clusters não circulares
Isto deixa claro que os GMMs abordam as duas principais questões práticas com k-means encontradas
anteriormente.
Escolhendo o tipo de
covariância Se você observar os detalhes dos ajustes anteriores, verá que a opção covariance_type
foi definida de forma diferente em cada um. Este hiperparâmetro controla os graus de liberdade na
forma de cada cluster; é essencial definir isso cuidadosamente para qualquer problema. O padrão é
covariance_type="diag", o que significa que o tamanho do cluster ao longo de cada dimensão pode ser
definido de forma independente, com a elipse resultante restrita para se alinhar aos eixos. Um modelo
um pouco mais simples e rápido é cova riance_type="spherical", que restringe a forma do cluster de
forma que todas as dimensões sejam iguais. O agrupamento resultante terá características semelhantes
às do k-means, embora não seja totalmente equivalente. Um modelo mais complicado e
computacionalmente caro (especialmente à medida que o número de dimensões aumenta) é usar
covariance_type="full", que permite que cada cluster seja modelado como uma elipse com orientação
arbitrária.
Podemos ver uma representação visual dessas três opções para um único cluster na Figura 5-131:
Como exemplo, considere alguns dados gerados a partir da função make_moons do Scikit-Learn
(visualizada na Figura 5-132), que vimos em “Em profundidade: agrupamento k-Means” na página 462:
Se tentarmos ajustar isso a um GMM de dois componentes visto como um modelo de agrupamento, os resultados
não serão particularmente úteis (Figura 5-133):
Figura 5-133. GMM de dois componentes adequado para clusters não lineares
Mas se usarmos muito mais componentes e ignorarmos os rótulos dos clusters, encontraremos um ajuste que está
muito mais próximo dos dados de entrada (Figura 5-134):
Figura 5-134. Usando muitos clusters GMM para modelar a distribuição de pontos
Aqui a mistura de 16 gaussianas não serve para encontrar grupos separados de dados, mas sim
para modelar a distribuição global dos dados de entrada. Este é um modelo generativo de
distribuição, o que significa que o GMM nos dá a receita para gerar novos dados aleatórios
distribuídos de forma semelhante à nossa entrada. Por exemplo, aqui estão 400 novos pontos
extraídos deste ajuste do GMM de 16 componentes aos nossos dados originais (Figura 5-135):
Quantos componentes?
O fato de o GMM ser um modelo generativo nos dá um meio natural de determinar o número
ideal de componentes para um determinado conjunto de dados. Um modelo generativo é
inerentemente uma distribuição de probabilidade para o conjunto de dados e, portanto, podemos
simplesmente avaliar a probabilidade dos dados no modelo, usando validação cruzada para
evitar ajuste excessivo. Outra forma de corrigir o overfitting é ajustar as probabilidades do
modelo usando algum critério analítico, como o critério de informação de Akaike (AIC) ou o
critério de informação bayesiano (BIC). O estimador GMM do Scikit-Learn, na verdade, inclui
métodos integrados que calculam ambos e, portanto, é muito fácil operar com essa abordagem.
Vejamos o AIC e o BIC como uma função do número de componentes do GMM para nosso
conjunto de dados lunares (Figura 5-136):
O número ideal de clusters é o valor que minimiza o AIC ou o BIC, dependendo da aproximação
que desejamos usar. A AIC diz-nos que a nossa escolha de 16 componentes foi provavelmente
demasiada: cerca de 8 a 12 componentes teriam sido um
melhor escolha. Como é típico neste tipo de problema, o BIC recomenda um modelo mais simples.
Observe o ponto importante: esta escolha do número de componentes mede quão bem o GMM funciona como
estimador de densidade, e não quão bem funciona como algoritmo de agrupamento. Eu encorajo você a pensar
no GMM principalmente como um estimador de densidade e usá-lo para agrupamento somente quando
necessário em conjuntos de dados simples.
ver um exemplo simples de utilização do GMM como modelo generativo de dados para criar novas amostras a
partir da distribuição definida pelos dados de entrada. Aqui seguiremos essa ideia e geraremos novos dígitos
manuscritos a partir do corpus de dígitos padrão que usamos antes.
Para começar, vamos carregar os dados de dígitos usando as ferramentas de dados do Scikit-Learn:
A seguir, vamos representar graficamente os primeiros 100 deles para lembrar exatamente o que estamos vendo
(Figura 5-137):
Temos quase 1.800 dígitos em 64 dimensões e podemos construir um GMM sobre eles para gerar mais. GMMs
podem ter dificuldade em convergir em um espaço dimensional tão alto, então começaremos com um algoritmo
de redução de dimensionalidade invertível nos dados. Aqui usaremos um PCA simples, solicitando que preserve
99% da variação nos dados projetados:
O resultado são 41 dimensões, uma redução de quase 1/3 e quase nenhuma perda de informação.
Dados esses dados projetados, vamos usar o AIC para obter uma medida do número de componentes
do GMM que devemos usar (Figura 5-138):
Agora podemos extrair amostras de 100 novos pontos dentro deste espaço projetado de 41
dimensões, usando o GMM como modelo generativo:
Figura 5-138. Curva AIC para escolher o número apropriado de componentes do GMM
Finalmente, podemos usar a transformada inversa do objeto PCA para construir os novos
dígitos (Figura 5-139):
Em [24]: dígitos_new = pca.inverse_transform(data_new)
plot_digits(digits_new)
Considere o que fizemos aqui: dada uma amostragem de dígitos manuscritos, modelamos a
distribuição desses dados de tal forma que podemos gerar novas amostras de dígitos a partir dos
dados: estes são “dígitos manuscritos” que não são individualizados. – aparecem no conjunto de
dados original, mas capturam as características gerais dos dados de entrada conforme modelados
pelo modelo de mistura. Tal modelo generativo de dígitos pode ser muito útil como componente
de um classificador generativo bayesiano, como veremos na próxima seção.
Na seção anterior abordamos modelos de mistura gaussiana (GMM), que são uma espécie de
híbrido entre um estimador de agrupamento e um estimador de densidade. Lembre-se de que um
estimador de densidade é um algoritmo que pega um conjunto de dados D-dimensional e produz
uma estimativa da distribuição de probabilidade D-dimensional da qual esses dados são extraídos.
O algoritmo GMM consegue isso representando a densidade como uma soma ponderada das
distribuições gaussianas. A estimativa de densidade do kernel (KDE) é, em certo sentido, um
algoritmo que leva a ideia da mistura de gaussianas ao seu extremo lógico: ele usa uma mistura
que consiste em um componente gaussiano por ponto, resultando em um estimador de densidade
essencialmente não paramétrico. Nesta seção, exploraremos a motivação e os usos do KDE.
Começamos com as importações padrão:
Por exemplo, vamos criar alguns dados extraídos de duas distribuições normais:
x = fazer_dados(1000)
Vimos anteriormente que o histograma padrão baseado em contagem pode ser criado com a função
plt.hist() . Ao especificar o parâmetro normado do histograma, terminamos com um histograma
normalizado onde a altura das caixas não reflete as contagens, mas em vez disso reflete a densidade
de probabilidade (Figura 5-140):
Observe que para categorização igual, essa normalização simplesmente altera a escala no eixo y,
deixando as alturas relativas essencialmente as mesmas de um histograma construído a partir de
contagens. Esta normalização é escolhida de forma que a área total sob o histograma seja igual a 1,
como podemos confirmar observando a saída da função histograma:
Fora[4]: 1,0
Um dos problemas de usar um histograma como estimador de densidade é que a escolha do tamanho
e localização do compartimento pode levar a representações que possuem características
qualitativamente diferentes. Por exemplo, se olharmos para uma versão destes dados com apenas
20 pontos, a escolha de como desenhar as caixas pode levar a uma interpretação totalmente diferente dos dados!
Considere este exemplo (visualizado na Figura 5-141):
Em[5]: x = make_data(20)
caixas = np.linspace(-5, 10, 10)
Figura 5-141. O problema com os histogramas: a localização das caixas pode afetar a interpretação
À esquerda, o histograma deixa claro que se trata de uma distribuição bimodal. À direita, vemos uma
distribuição unimodal com cauda longa. Sem ver o código anterior, você provavelmente não imaginaria
que esses dois histogramas foram construídos a partir dos mesmos dados. Com isso em mente, como
confiar na intuição que os histogramas conferem?
E como podemos melhorar isso?
Voltando atrás, podemos pensar em um histograma como uma pilha de blocos, onde empilhamos um
bloco dentro de cada compartimento no topo de cada ponto do conjunto de dados. Vejamos isso
diretamente (Figura 5-142):
ax.set_xlim(-4, 8)
ax.set_ylim(-0,2, 8)
Fora[7]: (-0,2, 8)
O problema com nossos dois agrupamentos decorre do fato de que a altura da pilha de blocos
geralmente reflete não na densidade real dos pontos próximos, mas nas coincidências de como os
compartimentos se alinham com os pontos de dados. Este desalinhamento entre os pontos e seus
blocos é uma causa potencial dos maus resultados do histograma vistos aqui. Mas e se, em vez
de empilharmos os blocos alinhados com as caixas, empilhássemos os blocos alinhados com os
pontos que eles representam? Se fizermos isso, os blocos não ficarão alinhados, mas podemos
somar suas contribuições em cada local ao longo do eixo x para encontrar o resultado. Vamos
tentar isso (Figura 5-143):
Figura 5-143. Um “histograma” onde os blocos são centralizados em cada ponto individual; este é um
exemplo de estimativa de densidade do kernel
O resultado parece um pouco confuso, mas é um reflexo muito mais robusto das características reais dos
dados do que o histograma padrão. Ainda assim, as arestas não são esteticamente agradáveis, nem refletem
quaisquer propriedades verdadeiras dos dados. Para suavizá-los, podemos decidir substituir os blocos em
cada local por uma função suave, como uma Gaussiana. Vamos usar uma curva normal padrão em cada
ponto em vez de um bloco (Figura 5-144):
Este gráfico suavizado, com uma distribuição gaussiana contribuída na localização de cada ponto de entrada,
dá uma ideia muito mais precisa da forma da distribuição de dados e que tem muito menos variância (ou seja,
muda muito menos na resposta). diferenças na amostragem).
Esses dois últimos gráficos são exemplos de estimativa de densidade de kernel em uma dimensão: o primeiro
usa o chamado kernel “tophat” e o segundo usa um kernel gaussiano. Veremos agora a estimativa da densidade
do kernel com mais detalhes.
parâmetros livres da estimativa da densidade do kernel são o kernel, que especifica a forma da distribuição
colocada em cada ponto, e a largura de banda do kernel, que controla o tamanho do kernel em cada ponto. Na
prática, existem muitos kernels que você pode usar para uma estimativa de densidade de kernel: em particular,
a implementação do Scikit-Learn KDE suporta um dos seis kernels, sobre os quais você pode ler na
documentação de estimativa de densidade do Scikit-Learn .
Embora existam várias versões de estimativa de densidade de kernel implementadas em Python (principalmente
nos pacotes SciPy e StatsModels), prefiro usar a versão do Scikit-Learn devido à sua eficiência e flexibilidade.
Ele é implementado no estimador sklearn.neigh bors.KernelDensity , que lida com o KDE em múltiplas
dimensões com um dos seis kernels e uma entre algumas dezenas de métricas de distância. Como o KDE pode
ser bastante computacionalmente intensivo, o estimador Scikit-Learn usa um algoritmo baseado em árvore e
pode compensar o tempo de cálculo pela precisão usando os parâmetros atol (tolerância absoluta) e rtol
(tolerância relativa ) . Podemos determinar o
largura de banda do kernel, que é um parâmetro livre, usando as ferramentas padrão de validação
cruzada do Scikit-Learn, como veremos em breve.
Vamos primeiro ver um exemplo simples de replicação do gráfico anterior usando o Scikit-Learn
Estimador KernelDensity (Figura 5-145):
O resultado aqui é normalizado de modo que a área sob a curva seja igual a 1.
cruzada A escolha da largura de banda dentro do KDE é extremamente importante para encontrar uma
estimativa de densidade adequada, e é o botão que controla a compensação entre polarização e variância
na estimativa de densidade: uma largura de banda muito estreita leva a uma estimativa de alta variância
(ou seja, ajuste excessivo), onde a presença ou ausência de um único ponto faz uma grande diferença.
Uma largura de banda muito ampla leva a uma estimativa de alto viés (isto é, subajuste), onde a estrutura
dos dados é eliminada pelo kernel amplo.
Há uma longa história em estatística de métodos para estimar rapidamente a melhor largura de banda com base
em suposições bastante rigorosas sobre os dados: se você procurar as implementações do KDE nos pacotes SciPy
e StatsModels, por exemplo, você verá implementações baseadas em em algumas dessas regras.
Em contextos de aprendizado de máquina, vimos que esse ajuste de hiperparâmetros geralmente é feito
empiricamente por meio de uma abordagem de validação cruzada. Com isso em mente, o estimador KernelDen
sity no Scikit-Learn foi projetado de forma que possa ser usado diretamente nas ferramentas de pesquisa de grade
padrão do Scikit-Learn. Aqui usaremos GridSearchCV para otimizar a largura de banda do conjunto de dados
anterior. Como estamos analisando um conjunto de dados tão pequeno, usaremos a validação cruzada deixe um
de fora, o que minimiza a redução no tamanho do conjunto de treinamento para cada tentativa de validação cruzada:
Agora podemos encontrar a escolha da largura de banda que maximiza a pontuação (que neste caso o padrão é a
probabilidade de log):
Em[12]: grid.best_params_
A largura de banda ideal é muito próxima daquela que usamos no gráfico de exemplo anterior, onde a largura de
banda era 1,0 (ou seja, a largura padrão de scipy.stats.norm).
mais comum do KDE seja na representação gráfica de distribuições de pontos. Por exemplo, na biblioteca de
visualização Seaborn (discutida anteriormente em “Visualização com Seaborn” na página 311), o KDE é integrado
e usado automaticamente para ajudar a visualizar pontos em uma e duas dimensões.
Aqui veremos um uso um pouco mais sofisticado do KDE para visualização de distribuições. Faremos uso de alguns
dados geográficos que podem ser carregados com o Scikit-Learn: as distribuições geográficas de observações
registradas de dois mamíferos sul-americanos, Bradypus variegatus (a preguiça-de-garganta-marrom) e Microryzomys
minutus (o pequeno rato do arroz da floresta). .
dados = fetch_species_distributions()
Com esses dados carregados, podemos usar o kit de ferramentas Basemap (mencionado anteriormente em
“Dados geográficos com mapa base” na página 298) para traçar as localizações observadas dessas duas espécies no
mapa da América do Sul (Figura 5-146):
# localizações do
gráfico m.scatter(latlon[:, 1], latlon[:, 0], zorder=3,
c=espécie, cmap='arco-íris', latlon=True);
Infelizmente, isto não dá uma ideia muito boa da densidade das espécies, porque pontos na distribuição das espécies
podem sobrepor-se uns aos outros. Você pode não perceber olhando para este gráfico, mas há mais de 1.600 pontos
mostrados aqui!
Vamos usar a estimativa de densidade do kernel para mostrar esta distribuição de uma forma
mais interpretável: como uma indicação suave de densidade no mapa. Como o sistema de
coordenadas aqui está em uma superfície esférica e não em um plano plano, usaremos a métrica
de distância Haversine , que representará corretamente as distâncias em uma superfície curva.
Há um pouco de código clichê aqui (uma das desvantagens do kit de ferramentas do Mapa Base),
mas o significado de cada bloco de código deve ser claro (Figura 5-147):
In[15]:
# Configure a grade de dados para o gráfico de
contorno X, Y = np.meshgrid(xgrid[::5], ygrid[::5][::-1])
land_reference = data.coverages[6 ][::5, ::5] land_mask
= (land_reference > -9999).ravel() xy =
np.vstack([Y.ravel(), X.ravel()]).T xy =
np.radians( xy[máscara_de_terra])
Em comparação com o gráfico de dispersão simples que utilizámos inicialmente, esta visualização mostra uma
imagem muito mais clara da distribuição geográfica das observações destas duas espécies.
exemplo analisa a classificação generativa Bayesiana com o KDE e demonstra como usar a arquitetura Scikit-Learn
para criar um estimador personalizado.
Em “Em profundidade: classificação Naive Bayes” na página 382, demos uma olhada na classificação Bayesiana
ingênua, na qual criamos um modelo generativo simples para cada classe e usamos esses modelos para construir
um classificador rápido. Para Bayes ingênuo, o modelo generativo é um gaussiano simples alinhado ao eixo. Com
um algoritmo de estimativa de densidade como o KDE, podemos remover o elemento “ingênuo” e realizar a mesma
classificação com um modelo generativo mais sofisticado para cada classe. Ainda é uma classificação bayesiana,
mas não é mais
ingênuo.
2. Para cada conjunto, ajuste um KDE para obter um modelo generativo dos dados. Isso permite que qualquer
observação x e rótulo y calcule uma probabilidade P xy .
4. Para um ponto desconhecido x, a probabilidade posterior para cada classe é P yx P xy P y . A classe que
maximiza esta posterior é o rótulo atribuído ao ponto.
Parâmetros
----------
kernel=self.kernel).fit(Xi)
para Xi em training_sets]
self.logpriors_ = [np.log(Xi.shape[0] / X.shape[0]) para Xi em training_sets]
retornar a si mesmo
Parâmetros
----------
Cada estimador no Scikit-Learn é uma classe, e é mais conveniente que esta classe herde da classe
BaseEstimator , bem como do mixin apropriado, que fornece funcionalidade padrão. Por exemplo, entre
outras coisas, aqui o BaseEstima tor contém a lógica necessária para clonar/copiar um estimador para
uso em um procedimento de validação cruzada, e ClassifierMixin define um método score() padrão usado
por tais rotinas. Também fornecemos uma docstring, que será capturada pela funcionalidade de ajuda do
IPython (consulte “Ajuda e documentação no IPython” na página 3).
Este é o código real executado quando o objeto é instanciado com KDEClassi fier(). No Scikit-Learn, é
importante que a inicialização não contenha outras operações além de atribuir os valores passados por
nome a si mesmo. Isso se deve à lógica contida no BaseEstimator necessária para clonar e modificar
estimadores para validação cruzada, pesquisa em grade e outras funções. Da mesma forma, todos os
argumentos para __init__ devem ser explícitos; isto é, *args ou **kwargs devem ser evitados, pois não
serão tratados corretamente nas rotinas de validação cruzada.
kernel=self.kernel).fit(Xi)
para Xi em training_sets]
self.logpriors_ = [np.log(Xi.shape[0] / X.shape[0]) para Xi em training_sets]
retornar a si mesmo
Aqui encontramos as classes exclusivas nos dados de treinamento, treinamos um modelo KernelDensity
para cada classe e calculamos os antecedentes das classes com base no número de amostras de entrada.
Finalmente, fit() deve sempre retornar self para que possamos encadear comandos. Por exemplo:
Observe que cada resultado persistente do ajuste é armazenado com um sublinhado final (por exemplo,
self.logpriors_). Esta é uma convenção usada no Scikit-Learn para que você possa verificar rapidamente os
membros de um estimador (usando o preenchimento de tabulação do IPython) e ver exatamente quais membros
são adequados para os dados de treinamento.
Como este é um classificador probabilístico, primeiro implementamos predizer_proba(), que retorna uma matriz
de probabilidades de classe de formato [n_samples, n_classes]. A entrada [i, j] desta matriz é a probabilidade
posterior de que a amostra i seja membro da classe j, calculada multiplicando a probabilidade pela classe
anterior e normalizando.
Finalmente, o método predizer() usa essas probabilidades e simplesmente retorna a classe com a maior
probabilidade.
personalizado Vamos testar esse estimador personalizado em um problema que já vimos antes: a classificação
de dígitos manuscritos. Aqui carregaremos os dígitos e calcularemos a pontuação de validação cruzada para
uma variedade de larguras de banda candidatas usando o metaestimador GridSearchCV (consulte
“Hiperparâmetros e validação de modelo” na página 359 para obter mais informações sobre isso):
dígitos = carregar_dígitos()
A seguir, podemos representar graficamente a pontuação da validação cruzada em função da largura de banda
(Figura 5-148):
imprimir (grid.best_params_)
imprimir ('precisão =', grid.best_score_)
Vemos que esse classificador bayesiano não tão ingênuo atinge uma precisão de validação cruzada de
pouco mais de 96%; isso é comparado a cerca de 80% para a classificação Bayesiana ingênua:
Fora[19]: 0,81860038035501381
Um benefício desse classificador generativo é a interpretabilidade dos resultados: para cada amostra
desconhecida, não obtemos apenas uma classificação probabilística, mas também um modelo completo
da distribuição de pontos com os quais estamos comparando! Se desejado, isso oferece uma janela
intuitiva sobre as razões para uma classificação específica que algoritmos como SVMs e florestas
aleatórias tendem a obscurecer.
Se você quiser ir mais longe, existem algumas melhorias que podem ser feitas em nosso modelo de
classificador do KDE:
independentemente. • Poderíamos otimizar essas larguras de banda não com base em sua pontuação
de previsão, mas na probabilidade dos dados de treinamento sob o modelo generativo dentro de
cada classe (ou seja, usar as pontuações do próprio KernelDensity em vez da precisão da previsão
global ) .
Finalmente, se você quiser praticar a construção de seu próprio estimador, você pode tentar construir
um classificador bayesiano semelhante usando modelos de mistura gaussiana em vez do KDE.
Nesta seção, daremos uma olhada em uma dessas técnicas de extração de características, o
Histograma de Gradientes Orientados (HOG), que transforma pixels de imagem em uma
representação vetorial que é sensível a características de imagem amplamente informativas,
independentemente de fatores de confusão como iluminação. Usaremos esses recursos para
desenvolver um pipeline simples de detecção facial, usando algoritmos e conceitos de aprendizado
de máquina que vimos ao longo deste capítulo. Começamos com as importações padrão:
Recursos do HOG
2. Convolver a imagem com dois filtros sensíveis aos gradientes de brilho horizontais e verticais.
Eles capturam informações de borda, contorno e textura.
4. Normalize os histogramas em cada célula comparando com o bloco de células vizinhas. Isso suprime
ainda mais o efeito de iluminação na imagem.
5. Construa um vetor de características unidimensional a partir das informações de cada célula.
Um extrator HOG rápido está embutido no projeto Scikit-Image, e podemos experimentá-lo de forma
relativamente rápida e visualizar os gradientes orientados dentro de cada célula (Figura 5-149):
ax[1].imshow(hog_vis)
ax[1].set_title('visualização dos recursos do HOG');
esses recursos do HOG, podemos construir um algoritmo simples de detecção facial com qualquer estimador
Scikit-Learn; aqui usaremos uma máquina de vetores de suporte linear (consulte “Detalhes: Máquinas de
vetores de suporte” na página 405 se precisar de uma atualização sobre isso). As etapas são as seguintes:
5. Para uma imagem “desconhecida”, passe uma janela deslizante pela imagem, usando o
modelo para avaliar se aquela janela contém uma face ou não.
Vamos começar encontrando alguns exemplos de treinamento positivos que mostram uma variedade de faces.
Temos um conjunto fácil de dados para trabalhar – o conjunto de dados Labeled Faces in the Wild, que pode ser
baixado pelo Scikit-Learn:
Isso nos dá uma amostra de 13.000 imagens faciais para usar em treinamento.
Em seguida, precisamos de um conjunto de miniaturas de tamanhos semelhantes que não tenham um rosto.
Uma maneira de fazer isso é pegar qualquer corpus de imagens de entrada e extrair miniaturas delas em diversas
escalas. Aqui podemos usar algumas das imagens enviadas com o Scikit-Image, junto com o PatchExtractor do Scikit-
Learn:
Em [5]:
de sklearn.feature_extraction.image importar PatchExtractor
patches de retorno
Agora temos 30.000 patches de imagens adequados que não contêm rostos. Vamos dar
uma olhada em alguns deles para ter uma ideia de sua aparência (Figura 5-150):
In[6]: fig, ax = plt.subplots(6, 10)
para i, axi in enumerate(ax.flat):
axi.imshow(negative_patches[500 * i], cmap='gray') axi.axis('off')
Nossa esperança é que isso cubra suficientemente o espaço de “não-faces” que nosso
algoritmo provavelmente verá.
3. Combine conjuntos e extraia recursos do HOG.
Agora que temos essas amostras positivas e negativas, podemos combiná-las e calcular as
características do HOG. Esta etapa demora um pouco, porque os recursos do HOG
envolvem um cálculo não trivial para cada imagem:
Em [7]: da cadeia de importação de
itertools X_train = np.array([feature.hog(im)
para estou em cadeia (patches_positivos,
patches_negativos)])
y_train = np.zeros(X_train.shape[0])
y_train[:positivo_patches.shape[0]] = 1
Em[8]: X_train.shape
Ficamos com 43.000 amostras de treinamento em 1.215 dimensões e agora temos nossos dados em um formato
que podemos alimentar no Scikit-Learn!
A seguir, usaremos as ferramentas que exploramos neste capítulo para criar um classificador de patches de
miniatura. Para uma tarefa de classificação binária de alta dimensão, uma máquina de vetores de suporte linear é
uma boa escolha. Usaremos a linha arSVC do Scikit-Learn, pois em comparação ao SVC ela geralmente possui
melhor dimensionamento para grande número de amostras.
Primeiro, porém, vamos usar um simples Bayes gaussiano ingênuo para obter uma linha de base rápida:
Vemos que em nossos dados de treinamento, mesmo um simples algoritmo ingênuo de Bayes nos leva a mais de
90% de precisão. Vamos tentar a máquina de vetores de suporte, com uma pesquisa em grade sobre algumas
opções do parâmetro C:
Saída[10]: 0,98667684407744083
Em[11]: grid.best_params_
Agora que temos esse modelo instalado, vamos pegar uma nova imagem e ver como o modelo se sai. Usaremos
uma parte da imagem do astronauta para simplificar (veja a discussão sobre isso em “Advertências e Melhorias” na
página 512), e executaremos uma janela deslizante sobre ela e avaliaremos cada patch (Figura 5-151):
plt.imshow(test_image, cmap='cinza')
plt.axis('off');
A seguir, vamos criar uma janela que itera sobre patches desta imagem e calcular
Recursos do HOG para cada patch:
Finalmente, podemos pegar esses patches com recursos do HOG e usar nosso modelo para
avaliar se cada patch contém uma face:
Fora[15]: 33,0
Vemos que de quase 2.000 patches, encontramos 30 detecções. Vamos usar as informações
que temos sobre esses patches para mostrar onde eles estão em nossa imagem de teste,
desenhando-os como retângulos (Figura 5-152):
Todas as manchas detectadas se sobrepõem e encontram o rosto na imagem! Nada mal para
algumas linhas de Python.
aprofundar um pouco mais no código e nos exemplos anteriores, verá que ainda temos um pouco de
trabalho antes de podermos reivindicar um detector facial pronto para produção. Existem vários
problemas com o que fizemos e várias melhorias que poderiam ser feitas. Em particular:
Poderíamos imaginar resolver isso adicionando uma variedade maior de imagens ao conjunto de
treinamento negativo, e isso provavelmente traria alguma melhoria. Outra maneira de resolver isso
é usar uma abordagem mais direcionada, como a mineração negativa pesada. Na mineração
negativa pesada, pegamos um novo conjunto de imagens que nosso classificador não viu,
encontramos todos os patches que representam falsos positivos e os adicionamos explicitamente
como instâncias negativas no conjunto de treinamento antes de treinar novamente o classificador.
Para aprender mais sobre aprendizado de máquina em Python, sugiro alguns dos seguintes
recursos:
O site Scikit-Learn
O site Scikit-Learn possui uma impressionante variedade de documentação e exemplos que
cobrem alguns dos modelos discutidos aqui e muito, muito mais. Se você deseja uma breve
pesquisa sobre os algoritmos de aprendizado de máquina mais importantes e frequentemente
usados, este site é um bom lugar para começar.
Aprendizado de máquina
ministrado por Andrew Ng (Coursera), este é um curso online gratuito e ensinado de forma muito clara,
cobrindo os fundamentos do aprendizado de máquina de uma perspectiva algorítmica. Ele pressupõe
compreensão de matemática e programação em nível de graduação e percorre considerações detalhadas
de alguns dos algoritmos de aprendizado de máquina mais importantes. As tarefas de casa, que são
avaliadas por algoritmos, permitem que você mesmo implemente alguns desses modelos.
Esses recursos são mais técnicos do que o material apresentado neste livro, mas para realmente compreender
os fundamentos desses métodos é necessário um mergulho profundo na matemática por trás deles. Se você está
pronto para o desafio e para levar sua ciência de dados para o próximo nível, não hesite em mergulhar!
Índice
517
Machine Translated by Google
bagging, largura
de banda 426 (consulte largura de banda do Dados categóricos C , 376
kernel) operador de barra rótulos de classe (para ponto de dados),
(|), gráficos de 77 barras, 321 334 tarefas de classificação
Critério de informação bayesiano (BIC), 487 comandos mágicos para colar blocos, 11 comandos
Folha de estilo de Métodos Bayesianos para Hackers, 288 mágicos para execução externa, 12 perfis e tempo, 25-30
Teorema de Bayess, 383 tempo de trechos, 25-27 coeficiente
largura de banda do kernel de de determinação, 365
compensação de polarização e variância e, 497
518 | Índice
Machine Translated by Google
Índice | 519
Machine Translated by Google
quando usar, 214 exceções, controle, 20-22 k-significa fraquezas abordadas por, 477-480
advertências do algoritmo de maximização KDE e, 491
de expectativa (EM), Classificação gaussiana ingênua de Bayes, 351, 357,
467-470 GMM como generalização de, 383-386, 510
480-484 k-means clustering e, 465-476 exponenciais, 55 Regressão de processo gaussiano (GPR), 239 modelos
externos código, generativos, 383 dados
comandos mágicos para execução, geográficos, 298
Kit de ferramentas de mapa base para, 298
520 | Índice
Machine Translated by Google
iteração sobre grupos, 164 método matriz imutável, objeto de índice como, 106
transform(), 167 operação importação, preenchimento de guia para, 7
groupby() (Pandas), 161-170 objeto GroupBy e, Em objetos, IPython,
163-165 exemplo de agrupamento, alinhamento de índice
169 tabelas dinâmicas vs., 13 em DataFrame, 117
171 especificação de em Série, 116
chave dividida, 168 exemplo split- Objeto de índice (Pandas), 105-107
apply-combine , 161-163 como matriz imutável, 106
como conjunto ordenado,
H 106
indexação
dígitos manuscritos, reconhecimento de (ver
sofisticada, 78-85 (consulte
reconhecimento de caracteres)
também indexação sofisticada) hierárquico (consulte indexação hierárquica)
mineração negativa difícil, 513
Matrizes NumPy: acessando elementos únicos, 43
ajuda
Pandas, 107
IPython, 3-8
Objeto IndexSlice, 137
funções mágicas, 13
variáveis de indicador, 183
funções help(), 4
junção interna, 153
binnings hexagonais, 248
histórico de entrada/saída, IPython, 13-16
indexação hierárquica em
séries unidimensionais, 128-141 MultiIndex, Objetos In e Out, 13
plt.hist2d(), 247 Seaborn, 314-317 de guias, 6-7 ajuda e documentação, 3-8 histórico de
entrada/saída, 13-16 atalhos de
simples, 245-246 bidimensional,
teclado no shell, 8 lançamento de
247-249 conjuntos de validação,
360 Hunter, John, 217 notebook Jupyter, 2 lançamento de
shell, 2 comandos mágicos , 10-13
hiperparâmetros,
349 (ver também validação do notebook (consulte o
Índice | 521
Machine Translated by Google
rótulos/tarefa de
J jet colormap, 257 classificação de rotulagem, 333-335
522 | Índice
Machine Translated by Google
Índice | 523
Machine Translated by Google
524 | Índice
Machine Translated by Google
110 -215 seleção de dados em série, 107-110 passageiros do Titanic, 170 Exemplo de
dados de taxa de natalidade dos EUA,
Objeto DataFrame, 102-105 eval()
agregação e
e query(), 208-209 manipulação de
agrupamento de conjuntos de dados de
dados ausentes, 119-120 indexação
174-178 planetas,
hierárquica, 128-141
gráficos de 159
Objeto de índice, instalação
105-107 , 97 barras, 321 legendas de gráficos
escolhendo elementos para,
mesclando/juntando conjuntos de dados, 146-158
NaN e None in, 123 valores 251 personalização, 249-255 legendas
Índice | 525
Machine Translated by Google
526 | Índice
Machine Translated by Google
rotulagem, 230-232
gráficos de barras,
cores e estilos de linha, 226- 228
321 conjuntos de dados e tipos de gráficos,
Matplotlib, 224-232 simples
313-329 histogramas facetados,
(Matplotlib), 224-232 regressão linear
318 gráficos de fatores,
simples, 390-392 gráficos de dispersão simples
319 histogramas, KDE e densidades, 314-317 distribuições
Populações de cidades da
conjuntas, exemplo de 320
Califórnia, 249-254 Matplotlib, 233-237 plt.plot,
tempos de finalização de maratona, 322-329 Matplotlib vs.,
233-235 plt.plot vs.
par 311-313 gráficos, folha de
dispersão, 237 plt.
estilo 317 ,
dispersão, 235-237
visualização 289
com, 311-313 Seattle, previsão de
operação slice(), 183
tráfego de bicicletas em regressão linear, série
fatiamento
temporal 400-405 , 202-209
MultiIndex com índices classificados/não classificados,
137
Seattle, estatísticas de precipitação em,
Matrizes NumPy, 44-47
70 aprendizagem semissupervisionada, 333
Matrizes NumPy: acessando submatrizes, 44
Objeto de série (Pandas), 99-102 como
dicionário, 100, 107
Índice | 527
Machine Translated by Google
528 | Índice
Machine Translated by Google
(_), 15 funções universais (ufuncs), métodos usando expressões regulares, 181 exemplo
50-58 valor absoluto, 54 recursos avançados, 56 de banco de dados de receitas, 184-188
agregados, 57 aritmética tabelas de, 180-184
de array, 52 básicos, 51 acesso e fatiamento de itens vetorizados, 183
operadores de Vega/Vega-Lite, enredo de
comparação como, 71-73 violino 330 , mapa
exponenciais, de cores de 327 viridis, 258
55 alinhamento de índice, 116-118 Vispy, software
preservação de índice, de visualização 330 (ver Matplotlib) (ver Seaÿ
115 logaritmos, 55 operando em nascer)
Índice | 529
Machine Translated by Google
Sobre o autor
Jake VanderPlas é um usuário e desenvolvedor de longa data da pilha científica Python. Atualmente,
ele trabalha como diretor de pesquisa interdisciplinar na Universidade de Washington, conduz sua própria
pesquisa astronômica e passa seu tempo aconselhando e consultando cientistas locais de diversas áreas.
Colofão
O animal na capa do Python Data Science Handbook é um lagarto mexicano (Heloderma horridum), um
réptil encontrado no México e em partes da Guatemala. Ele e o monstro Gila (um parente próximo) são
os únicos lagartos venenosos do mundo. No entanto, este animal se alimenta principalmente de ovos,
por isso o veneno é usado como mecanismo de defesa.
Quando se sente ameaçado, o lagarto morde – e como não consegue libertar uma grande quantidade
de veneno de uma só vez, aperta firmemente as mandíbulas e usa um movimento de mastigação para
mover a toxina mais profundamente na ferida. Esta mordida e os efeitos posteriores do veneno são
extremamente dolorosos, embora raramente fatais para os humanos.
A palavra grega heloderma se traduz como “pele cravejada”, referindo-se à textura distinta da pele do
réptil. Essas saliências são osteodermas, cada uma contendo um pequeno pedaço de osso e servindo
como armadura protetora. O lagarto mexicano é preto com manchas e faixas amarelas. Possui cabeça
larga e cauda grossa que armazena gordura para ajudar o animal a sobreviver durante os meses quentes
de verão, quando está inativo. Em média, esses lagartos têm de 22 a 36 polegadas de comprimento e
pesam cerca de 1,8 quilo.
Tal como acontece com a maioria das cobras e lagartos, a língua do lagarto mexicano é o seu principal
órgão sensorial. Ele irá agitá-lo repetidamente para coletar partículas de cheiro do ambiente e detectar
uma presa (ou, durante a época de acasalamento, um parceiro em potencial). Quando a língua bifurcada
é retraída para dentro da boca, ela toca o órgão de Jacobson, um conjunto de células sensoriais que
identificam vários produtos químicos e feromônios.
O veneno do lagarto contém enzimas que foram sintetizadas para ajudar a tratar o diabetes, e mais
pesquisas farmacológicas estão em andamento. Está ameaçado pela perda de habitat, pela caça furtiva
para o comércio de animais de estimação e pela morte por moradores locais que simplesmente têm
medo dele. Este animal é protegido pela legislação dos dois países onde vive.
Muitos dos animais nas capas da O'Reilly estão ameaçados de extinção; todos eles são importantes
para o mundo. Para saber mais sobre como você pode ajudar, acesse pets.oreilly.com.
A imagem da capa é do Wood's Animate Creation. As fontes da capa são URW Typewriter e Guardian
Sans. A fonte do texto é Adobe Minion Pro; a fonte do título é Adobe Myriad Condensed; e a fonte do
código é Ubuntu Mono da Dalton Maag.