Guia definitivo para regressão logística em Python PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Guia definitivo para regressão logística em Python

Introdução

Às vezes confundido com regressão linear por novatos – devido ao compartilhamento do termo regressão - regressão logística é muito diferente de regressão linear. Enquanto a regressão linear prevê valores como 2, 2.45, 6.77 ou valores contínuos, tornando-o regressão algoritmo, regressão logística prevê valores como 0 ou 1, 1 ou 2 ou 3, que são valores discretos, tornando-o classificação algoritmo. Sim, chama-se regressão mas é um classificação algoritmo. Mais sobre isso em um momento.

Portanto, se o seu problema de ciência de dados envolve valores contínuos, você pode aplicar um regressão algoritmo (regressão linear é um deles). Caso contrário, se envolver a classificação de entradas, valores discretos ou classes, você pode aplicar uma classificação algoritmo (a regressão logística é um deles).

Neste guia, realizaremos a regressão logística em Python com a biblioteca Scikit-Learn. Também explicaremos por que a palavra "regressão" está presente no nome e como funciona a regressão logística.

Para isso, primeiro carregaremos os dados que serão classificados, visualizados e pré-processados. Em seguida, construiremos um modelo de regressão logística que entenderá esses dados. Este modelo será então avaliado e empregado para prever valores com base em novas entradas.

Motivação

A empresa para a qual você trabalha fez uma parceria com uma fazenda agrícola turca. Esta parceria envolve a venda de sementes de abóbora. As sementes de abóbora são muito importantes para a nutrição humana. Eles contêm uma boa proporção de carboidratos, gordura, proteína, cálcio, potássio, fósforo, magnésio, ferro e zinco.

Na equipe de ciência de dados, sua tarefa é dizer a diferença entre os tipos de sementes de abóbora apenas usando dados – ou classificação os dados de acordo com o tipo de semente.

A fazenda turca trabalha com dois tipos de sementes de abóbora, um é chamado Çercevelik e o outro Ürgüp Sivrisi.

Para classificar as sementes de abóbora, sua equipe seguiu o artigo de 2021 “O uso de métodos de aprendizado de máquina na classificação de sementes de abóbora (Cucurbita pepo L.). Recursos Genéticos e Evolução das Culturas” de Koklu, Sarigil e Ozbek – neste artigo, há uma metodologia para fotografar e extrair as medidas das sementes das imagens.

Após a conclusão do processo descrito no artigo, foram extraídas as seguintes medidas:

  • Área – o número de pixels dentro das bordas de uma semente de abóbora
  • Perímetro – a circunferência em pixels de uma semente de abóbora
  • Comprimento do Eixo Principal – também a circunferência em pixels de uma semente de abóbora
  • Comprimento do Eixo Menor – a pequena distância do eixo de uma semente de abóbora
  • Excentricidade – a excentricidade de uma semente de abóbora
  • Área Convexa – o número de pixels da menor casca convexa na região formada pela semente de abóbora
  • Extensão – a proporção de uma área de semente de abóbora para os pixels da caixa delimitadora
  • Diâmetro Equivalente – a raiz quadrada da multiplicação da área da semente de abóbora por quatro dividido por pi
  • Compacidade – a proporção da área da semente de abóbora em relação à área do círculo com a mesma circunferência
  • Solidity – a condição convexa e convexa das sementes de abóbora
  • Esfericidade – a ovalização das sementes de abóbora sem considerar as distorções das bordas
  • Proporção da tela – a proporção de aspecto das sementes de abóbora

Essas são as medidas com as quais você tem que trabalhar. Além das medições, há também a Aula rótulo para os dois tipos de sementes de abóbora.

Para começar a classificar as sementes, vamos importar os dados e começar a examiná-los.

Entendendo o conjunto de dados

Observação: Você pode baixar o conjunto de dados de abóbora SUA PARTICIPAÇÃO FAZ A DIFERENÇA.

Depois de baixar o conjunto de dados, podemos carregá-lo em uma estrutura de dataframe usando o pandas biblioteca. Como é um arquivo excel, usaremos o read_excel() método:

import pandas as pd

fpath = 'dataset/pumpkin_seeds_dataset.xlsx' 
df = pd.read_excel(fpath)

Uma vez que os dados são carregados, podemos dar uma olhada rápida nas primeiras 5 linhas usando o head() método:

df.head() 

Isto resulta em:

	Area 	Perimeter 	Major_Axis_Length 	Minor_Axis_Length 	Convex_Area 	Equiv_Diameter 	Eccentricity 	Solidity 	Extent 	Roundness 	Aspect_Ration 	Compactness 	Class
0 	56276 	888.242 	326.1485 			220.2388 			56831 			267.6805 		0.7376 			0.9902 		0.7453 	0.8963 		1.4809 			0.8207 			Çerçevelik
1 	76631 	1068.146 	417.1932 			234.2289 			77280 			312.3614 		0.8275 			0.9916 		0.7151 	0.8440 		1.7811 			0.7487 			Çerçevelik
2 	71623 	1082.987 	435.8328 			211.0457 			72663 			301.9822 		0.8749 			0.9857 		0.7400 	0.7674 		2.0651 			0.6929 			Çerçevelik
3 	66458 	992.051 	381.5638 			222.5322 			67118 			290.8899 		0.8123 			0.9902 		0.7396 	0.8486 		1.7146 			0.7624 			Çerçevelik
4 	66107 	998.146 	383.8883 			220.4545 			67117 			290.1207 		0.8187 			0.9850 		0.6752 	0.8338 		1.7413 			0.7557 			Çerçevelik

Aqui, temos todas as medidas em suas respectivas colunas, nosso característicase também o Aula coluna, nosso alvo, que é o último no dataframe. Podemos ver quantas medidas temos usando o shape atributo:

df.shape 

A saída é:

(2500, 13)

O resultado da forma nos diz que existem 2500 entradas (ou linhas) no conjunto de dados e 13 colunas. Como sabemos que há uma coluna de destino, isso significa que temos 12 colunas de recursos.

Agora podemos explorar a variável alvo, a semente de abóbora Class. Como vamos prever essa variável, é interessante ver quantas amostras de cada semente de abóbora temos. Normalmente, quanto menor a diferença entre o número de instâncias em nossas classes, mais equilibrada é nossa amostra e melhores nossas previsões.

Esta inspeção pode ser feita contando cada amostra de semente com o value_counts() método:

df['Class'].value_counts() 

O código acima exibe:

Çerçevelik       1300
Ürgüp Sivrisi    1200
Name: Class, dtype: int64

Podemos ver que existem 1300 amostras do Çercevelik sementes e 1200 amostras do Ürgüp Sivrisi semente. Observe que a diferença entre eles é de 100 amostras, uma diferença muito pequena, o que é bom para nós e indica que não há necessidade de rebalancear o número de amostras.

Vejamos também as estatísticas descritivas de nossos recursos com o describe() método para ver quão bem distribuídos são os dados. Também vamos transpor a tabela resultante com T para facilitar a comparação entre estatísticas:

df.describe().T

A tabela resultante é:

					count 	mean 			std 			min 		25% 			50% 			75% 			max
Area 				2500.0 	80658.220800 	13664.510228 	47939.0000 	70765.000000 	79076.00000 	89757.500000 	136574.0000
Perimeter 			2500.0 	1130.279015 	109.256418 		868.4850 	1048.829750 	1123.67200 		1203.340500 	1559.4500
Major_Axis_Length 	2500.0 	456.601840 		56.235704 		320.8446 	414.957850 		449.49660 		492.737650 		661.9113
Minor_Axis_Length 	2500.0 	225.794921 		23.297245 		152.1718 	211.245925 		224.70310 		240.672875 		305.8180
Convex_Area 		2500.0 	81508.084400 	13764.092788 	48366.0000 	71512.000000 	79872.00000 	90797.750000 	138384.0000
Equiv_Diameter 		2500.0 	319.334230 		26.891920 		247.0584 	300.167975 		317.30535 		338.057375 		417.0029
Eccentricity 		2500.0 	0.860879 		0.045167 		0.4921 		0.831700 		0.86370 		0.897025 		0.9481
Solidity 			2500.0 	0.989492 		0.003494 		0.9186 		0.988300 		0.99030 		0.991500 		0.9944
Extent 				2500.0 	0.693205 		0.060914 		0.4680 		0.658900 		0.71305 		0.740225 		0.8296
Roundness 			2500.0 	0.791533 		0.055924 		0.5546 		0.751900 		0.79775 		0.834325 		0.9396
Aspect_Ration 		2500.0 	2.041702 		0.315997 		1.1487 		1.801050 		1.98420 		2.262075 		3.1444
Compactness 		2500.0 	0.704121 		0.053067 		0.5608 		0.663475 		0.70770 		0.743500 		0.9049

Ao olhar para a tabela, ao comparar o significar e desvio padrão (std), pode-se observar que a maioria das características possui uma média que está longe do desvio padrão. Isso indica que os valores dos dados não estão concentrados em torno do valor médio, mas mais dispersos em torno dele – em outras palavras, eles têm alta variabilidade.

Também, ao olhar para o mínimo (min) e máximo (max) colunas, alguns recursos, como Area e Convex_Area, têm grandes diferenças entre os valores mínimo e máximo. Isso significa que essas colunas têm dados muito pequenos e também valores de dados muito grandes, ou maior amplitude entre os valores dos dados.

Com alta variabilidade, alta amplitude e recursos com diferentes unidades de medida, a maioria dos nossos dados se beneficiaria de ter a mesma escala para todos os recursos ou ser dimensionado. O dimensionamento de dados centralizará os dados em torno da média e reduzirá sua variação.

Esse cenário provavelmente também indica que existem valores discrepantes e extremos nos dados. Então, é melhor ter alguns tratamento atípico além de dimensionar os dados.

Existem alguns algoritmos de aprendizado de máquina, por exemplo, algoritmos baseados em árvore, como Classificação Florestal Aleatória, que não são afetados por alta variação de dados, valores discrepantes e valores extremos. Regressão logística é diferente, é baseado em uma função que categoriza nossos valores, e os parâmetros dessa função podem ser afetados por valores que estão fora da tendência geral dos dados e possuem alta variação.

Vamos entender mais sobre regressão logística daqui a pouco quando começarmos a implementá-la. Por enquanto, podemos continuar explorando nossos dados.

Observação: Existe um ditado popular em Ciência da Computação: “Entra lixo, sai lixo” (GIGO), que é adequado para aprendizado de máquina. Isso significa que quando temos dados ruins – medições que não descrevem os fenômenos em si, dados que não foram entendidos e bem preparados de acordo com o tipo de algoritmo ou modelo, provavelmente gerarão uma saída incorreta que não funcionará um dia a dia.
Essa é uma das razões pelas quais explorar, entender os dados e como o modelo escolhido funciona são tão importantes. Ao fazer isso, podemos evitar colocar lixo em nosso modelo – colocando valor nele e extraindo valor.

Visualizando os dados

Até agora, com as estatísticas descritivas, temos um instantâneo um tanto abstrato de algumas qualidades dos dados. Outro passo importante é visualizá-lo e confirmar nossa hipótese de alta variância, amplitude e outliers. Para ver se o que observamos até agora aparece nos dados, podemos traçar alguns gráficos.

Também é interessante ver como as características se relacionam com as duas classes que serão previstas. Para isso, vamos importar o seaborn embalar e usar o pairplot gráfico para ver a distribuição de cada recurso e cada separação de classe por recurso:

import seaborn as sns


sns.pairplot(data=df, hue='Class')

Observação: O código acima pode demorar um pouco para ser executado, pois o pairplot combina gráficos de dispersão de todos os recursos (ele pode) e também exibe as distribuições de recursos.

Olhando para o gráfico de pares, podemos ver que na maioria dos casos os pontos do Çerçevelik classe são claramente separados dos pontos da Ürgüp Sivrisi classe. Ou os pontos de uma classe estão à direita quando os outros estão à esquerda, ou alguns estão para cima enquanto os outros estão para baixo. Se tivéssemos que usar algum tipo de curva ou linha para separar as classes, isso mostra que é mais fácil separá-las, se fossem misturadas, a classificação seria uma tarefa mais difícil.

No Eccentricity, Compactness e Aspect_Ration colunas, alguns pontos que estão “isolados” ou desviando da tendência geral dos dados – outliers – também são facilmente identificados.

Ao olhar para a diagonal do canto superior esquerdo para o canto inferior direito do gráfico, observe que as distribuições de dados também são codificadas por cores de acordo com nossas classes. As formas de distribuição e a distância entre as duas curvas são outros indicadores de quão separáveis ​​elas são – quanto mais distantes uma da outra, melhor. Na maioria dos casos, não são sobrepostos, o que implica que são mais fáceis de separar, contribuindo também para a nossa tarefa.

Em sequência, também podemos plotar os boxplots de todas as variáveis ​​com o sns.boxplot() método. Na maioria das vezes, é útil orientar os boxplots horizontalmente, para que as formas dos boxplots sejam as mesmas das formas de distribuição, podemos fazer isso com o orient argumento:


sns.boxplot(data=df, orient='h') 

Guia definitivo para regressão logística em Python PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

No gráfico acima, observe que Area e Convex_Area têm uma magnitude tão alta quando comparadas às magnitudes das outras colunas, que esmagam os outros boxplots. Para poder ver todos os boxplots, podemos dimensionar os recursos e plotá-los novamente.

Antes de fazer isso, vamos apenas entender que se existem valores de características que estão intimamente relacionados a outros valores, por exemplo – se existem valores que também aumentam quando outros valores de características aumentam, tendo um correlação positiva; ou se existem valores que fazem o contrário, ficam menores enquanto outros valores ficam menores, tendo um correlação negativa.

É importante observar isso porque ter relacionamentos fortes nos dados pode significar que algumas colunas foram derivadas de outras colunas ou têm um significado semelhante ao nosso modelo. Quando isso acontece, os resultados do modelo podem estar superestimados e queremos resultados mais próximos da realidade. Se houver correlações fortes, isso também significa que podemos reduzir o número de recursos e usar menos colunas, tornando o modelo mais parcimonioso.

Observação: A correlação padrão calculada com o corr() método é o Coeficiente de correlação de Pearson. Este coeficiente é indicado quando os dados são quantitativos, normalmente distribuídos, não possuem outliers e possuem uma relação linear.

Outra opção seria calcular Coeficiente de correlação de Spearman. O coeficiente de Spearman é usado quando os dados são ordinais, não lineares, têm qualquer distribuição e possuem valores discrepantes. Observe que nossos dados não se encaixam totalmente nas suposições de Pearson ou Spearman (há também mais métodos de correlação, como o de Kendall). Como nossos dados são quantitativos e é importante medirmos sua relação linear, usaremos o coeficiente de Pearson.

Vamos dar uma olhada nas correlações entre as variáveis ​​e então podemos passar para o pré-processamento dos dados. Vamos calcular as correlações com o corr() método e visualizá-los com Seaborn's heatmap(). O tamanho padrão do mapa de calor tende a ser pequeno, então importaremos matplotlib (mecanismo/biblioteca de visualização geral no qual o Seaborn é construído) e altere o tamanho com figsize:

import matplotlib.pyplot as plt
plt.figure(figsize=(15, 10))

correlations = df.corr()
sns.heatmap(correlations, annot=True) 

Guia definitivo para regressão logística em Python PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Neste mapa de calor, os valores mais próximos de 1 ou -1 são os valores aos quais precisamos prestar atenção. O primeiro caso denota uma alta correlação positiva e o segundo, uma alta correlação negativa. Ambos os valores, se não superiores a 0.8 ou -0.8 serão benéficos para o nosso modelo de regressão logística.

Quando há altas correlações como a de 0.99 entre Aspec_Ration e Compactness, isso significa que podemos optar por usar apenas Aspec_Ration ou apenas Compactness, em vez de ambos (já que eles seriam quase iguais preditores de cada um). O mesmo vale para Eccentricity e Compactness com uma -0.98 correlação, para Area e Perimeter com uma 0.94 correlação e algumas outras colunas.

Pré-processamento dos dados

Como já exploramos os dados há algum tempo, podemos começar a pré-processá-los. Por enquanto, vamos usar todos os recursos para a previsão de classe. Depois de obter um primeiro modelo, uma linha de base, podemos remover algumas das colunas altamente correlacionadas e compará-las com a linha de base.

As colunas de recursos serão nossas X data e a coluna de classe, nosso y dados de destino:

y = df['Class']
X = df.drop(columns=['Class'], axis=1)

Transformando recursos categóricos em recursos numéricos

Em relação ao nosso Class column – seus valores não são números, isso significa que também precisamos transformá-los. Há muitas maneiras de fazer essa transformação; aqui, usaremos o replace() método e substituir Çerçevelik para 0 e Ürgüp Sivrisi para 1.

y = y.replace('Çerçevelik', 0).replace('Ürgüp Sivrisi', 1)

Mantenha o mapeamento em mente! Ao ler os resultados do seu modelo, você desejará convertê-los de volta pelo menos em sua mente, ou de volta ao nome da classe para outros usuários.

Dividindo dados em conjuntos de treinamento e teste

Em nossa exploração, notamos que os recursos precisavam ser dimensionados. Se fizéssemos o dimensionamento agora, ou de forma automática, dimensionariamos os valores com todo o X e y. Nesse caso, apresentaríamos Vazamento de informações, uma vez que os valores do conjunto de testes em breve teriam impactado o dimensionamento. O vazamento de dados é uma causa comum de resultados irreprodutíveis e alto desempenho ilusório de modelos de ML.

Pensar no dimensionamento mostra que precisamos primeiro dividir X e y dados ainda mais em conjuntos de treinamento e teste e, em seguida, para caber um scaler no conjunto de treinamento, e para transformar tanto o conjunto de treinamento quanto o de teste (sem que o conjunto de teste tenha impacto no escalador que faz isso). Para isso, usaremos o Scikit-Learn train_test_split() método:

from sklearn.model_selection import train_test_split
SEED = 42 

X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size=.25, 
                                                    random_state=SEED)

Configuração test_size=.25 é garantir que estamos usando 25% dos dados para teste e 75% para treinamento. Isso pode ser omitido, uma vez que é a divisão padrão, mas a Pitônico maneira de escrever código aconselha que ser “explícito é melhor que implícito”.

Observação: A frase “explícito é melhor que implícito” é uma referência a O Zen de Python, ou PEP20. Ele apresenta algumas sugestões para escrever código Python. Se essas sugestões forem seguidas, o código é considerado Pitônico. Você pode saber mais sobre isso SUA PARTICIPAÇÃO FAZ A DIFERENÇA.

Depois de dividir os dados em conjuntos de treinamento e teste, é uma boa prática verificar quantos registros existem em cada conjunto. Isso pode ser feito com o shape atributo:

X_train.shape, X_test.shape, y_train.shape, y_test.shape

Isso exibe:

((1875, 12), (625, 12), (1875,), (625,))

Podemos ver que após a divisão, temos 1875 registros para treinamento e 625 para teste.

Dados de dimensionamento

Assim que tivermos nossos conjuntos de treinamento e teste prontos, podemos prosseguir para dimensionar os dados com o Scikit-Learn StandardScaler objeto (ou outros scalers fornecidos pela biblioteca). Para evitar vazamentos, o destartarizador é instalado no X_train os dados e os valores do trem são então usados ​​para dimensionar - ou transformar - os dados do trem e do teste:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

Como você normalmente chamará:

scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

As duas primeiras linhas podem ser recolhidas com um singular fit_transform() call, que encaixa o scaler no set e o transforma de uma só vez. Agora podemos reproduzir os gráficos boxplot para ver a diferença após dimensionar os dados.

Considerando que o dimensionamento remove os nomes das colunas, antes da plotagem, podemos organizar os dados do trem em um dataframe com os nomes das colunas novamente para facilitar a visualização:

column_names = df.columns[:12] 
X_train = pd.DataFrame(X_train, columns=column_names)

sns.boxplot(data=X_train, orient='h')

Guia definitivo para regressão logística em Python PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Finalmente podemos ver todos os nossos boxplots! Observe que todos eles têm outliers, e as feições que apresentam uma distribuição mais distante da normal (que possuem curvas enviesadas para a esquerda ou para a direita), como Solidity, Extent, Aspect_Ration e Compactedness, são os mesmos que apresentaram maiores correlações.

Removendo valores discrepantes com o método IQR

Já sabemos que a regressão logística pode ser impactada por outliers. Uma das formas de tratá-los é usar um método chamado Intervalo Interquartil or AIQ. O passo inicial do método IQR é dividir nossos dados de trem em quatro partes, chamadas quartis. O primeiro quartil, Q1, equivale a 25% dos dados, o segundo, Q2, para 50%, o terceiro, Q3, para 75%, e o último, Q4, a 100%. As caixas no boxplot são definidas pelo método IQR e são uma representação visual dele.

Considerando um boxplot horizontal, a linha vertical à esquerda marca 25% dos dados, a linha vertical no meio, 50% dos dados (ou a mediana), e a última linha vertical à direita, 75% dos dados . Quanto mais uniformes forem os dois quadrados definidos pelas linhas verticais – ou quanto mais a linha vertical mediana estiver no meio – significa que nossos dados estão mais próximos da distribuição normal ou menos assimétricos, o que é útil para nossa análise.

Além da caixa IQR, também existem linhas horizontais em ambos os lados. Essas linhas marcam os valores mínimos e máximos de distribuição definidos por

$$
Mínimo = Q1 - 1.5*IQR
$$

e

$$
Máximo = Q3 + 1.5*IQR
$$

IQR é exatamente a diferença entre Q3 e Q1 (ou Q3 – Q1) e é o ponto mais central dos dados. Por isso, ao encontrar o IQR, acabamos filtrando os outliers nas extremidades dos dados, ou nos pontos mínimo e máximo. Os gráficos de caixa nos dão uma prévia de qual será o resultado do método IQR.

Guia definitivo para regressão logística em Python PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Podemos usar pandas quantile() método para encontrar nossos quantis, e iqr do scipy.stats pacote para obter o intervalo de dados interquartis para cada coluna:

from scipy.stats import iqr

Q1 = X_train.quantile(q=.25)
Q3 = X_train.quantile(q=.75)

IQR = X_train.apply(iqr)

Agora temos Q1, Q3 e IQR, podemos filtrar os valores mais próximos da mediana:


minimum = X_train < (Q1-1.5*IQR)
maximum = X_train > (Q3+1.5*IQR)


filter = ~(minimum | maximum).any(axis=1)


X_train = X_train[filter]

Depois de filtrar nossas linhas de treinamento, podemos ver quantas delas ainda estão nos dados com shape:

X_train.shape

Isto resulta em:

(1714, 12)

Podemos ver que o número de linhas passou de 1875 para 1714 após a filtragem. Isso significa que 161 linhas continham valores discrepantes ou 8.5% dos dados.

Observação: É aconselhável que a filtragem de outliers, remoção de valores NaN e outras ações que envolvam filtragem e limpeza de dados permaneçam abaixo ou até 10% dos dados. Tente pensar em outras soluções se sua filtragem ou remoção exceder 10% de seus dados.

Depois de remover os outliers, estamos quase prontos para incluir os dados no modelo. Para o ajuste do modelo, usaremos os dados do trem. X_train é filtrado, mas e quanto y_train?

y_train.shape

Isso resulta em:

(1875,)

Observe que y_train ainda tem 1875 linhas. Precisamos igualar o número de y_train linhas para o número de X_train linhas e não apenas arbitrariamente. Precisamos remover os valores y das instâncias de sementes de abóbora que removemos, que provavelmente estão espalhadas pelo y_train definir. O filtrado X_train ainda tem seus índices originais e o índice tem lacunas onde removemos outliers! Podemos então usar o índice de X_train DataFrame para procurar os valores correspondentes em y_train:

y_train = y_train.iloc[X_train.index]

Depois de fazer isso, podemos olhar para o y_train forma novamente:

y_train.shape

Quais saídas:

(1714,)

Confira nosso guia prático e prático para aprender Git, com práticas recomendadas, padrões aceitos pelo setor e folha de dicas incluída. Pare de pesquisar comandos Git no Google e realmente aprender -lo!

Agora, y_train também tem 1714 linhas e são as mesmas que o X_train linhas. Finalmente estamos prontos para criar nosso modelo de regressão logística!

Implementando o Modelo de Regressão Logística

A parte difícil está feita! O pré-processamento geralmente é mais difícil do que o desenvolvimento de modelos, quando se trata de usar bibliotecas como Scikit-Learn, que simplificaram a aplicação de modelos de ML para apenas algumas linhas.

Primeiro, importamos o LogisticRegression class e instanciá-lo, criando um LogisticRegression objeto:

from sklearn.linear_model import LogisticRegression
logreg = LogisticRegression(random_state=SEED)

Em segundo lugar, ajustamos nossos dados de trem ao logreg modelo com o fit() método e prever nossos dados de teste com o predict() método, armazenando os resultados como y_pred:



logreg.fit(X_train.values, y_train)
y_pred = logreg.predict(X_test)

Já fizemos previsões com nosso modelo! Vamos olhar para as primeiras 3 linhas em X_train para ver quais dados usamos:

X_train[:3]

O código acima gera:

       Area          Perimeter     Major_Axis_Length    Minor_Axis_Length    Convex_Area   Equiv_Diameter       Eccentricity  Solidity      Extent        Roundness     Aspect_Ration        Compactness
0      -1.098308     -0.936518     -0.607941            -1.132551            -1.082768     -1.122359            0.458911      -1.078259     0.562847      -0.176041     0.236617             -0.360134
1      -0.501526     -0.468936     -0.387303            -0.376176            -0.507652     -0.475015            0.125764      0.258195      0.211703      0.094213      -0.122270            0.019480
2      0.012372      -0.209168     -0.354107            0.465095              0.003871      0.054384            -0.453911     0.432515      0.794735      0.647084      -0.617427            0.571137

E nas primeiras 3 previsões em y_pred para ver os resultados:

y_pred[:3] 

Isto resulta em:

array([0, 0, 0])

Para essas três linhas, nossas previsões eram de que eram sementes de primeira classe, Çerçevelik.

Com o regressão logística, em vez de prever a classe final, como 0, também podemos prever a probabilidade que a linha tem de pertencer ao 0 classe. Isso é o que realmente acontece quando a regressão logística classifica os dados, e a predict() O método então passa essa previsão por um limite para retornar uma classe “difícil”. Para prever a probabilidade de pertencer a uma classe, predict_proba() é usado:

y_pred_proba = logreg.predict_proba(X_test)

Vamos também dar uma olhada nos primeiros 3 valores das previsões de probabilidades y:

y_pred_proba[:3] 

Quais saídas:

        # class 0   class 1   
array([[0.54726628, 0.45273372],
       [0.56324527, 0.43675473],
       [0.86233349, 0.13766651]])

Agora, em vez de três zeros, temos uma coluna para cada classe. Na coluna à esquerda, começando com 0.54726628, são as probabilidades dos dados pertencentes à classe 0; e na coluna da direita, começando com 0.45273372, é a probabilidade de pertencer à classe 1.

Observação: Essa diferença de classificação também é conhecida como difícil e macio predição. A previsão rígida encaixota a previsão em uma classe, enquanto as previsões suaves emitem a probabilidade da instância pertencente a uma classe.

Há mais informações sobre como a saída prevista foi feita. Na verdade não era 0, mas uma chance de 55% de classe 0, e uma chance de 45% de classe 1. Isso mostra como os três primeiros X_test pontos de dados, pertencentes à classe 0, são realmente claros apenas em relação ao terceiro ponto de dados, com uma probabilidade de 86% – e não tanto para os dois primeiros pontos de dados.

Ao comunicar descobertas usando métodos de ML – normalmente é melhor retornar uma classe soft e a probabilidade associada como a "confiança" dessa classificação.

Falaremos mais sobre como isso é calculado quando nos aprofundarmos no modelo. Neste momento, podemos prosseguir para a próxima etapa.

Avaliando o Modelo com Relatórios de Classificação

A terceira etapa é ver como o modelo se comporta nos dados de teste. Podemos importar Scikit-Learn classification_report() e passar o nosso y_test e y_pred como argumentos. Depois disso, podemos imprimir sua resposta.

O relatório de classificação contém as métricas de classificação mais usadas, como precisão, recordar, pontuação f1 e precisão.

  1. Precisão: para entender quais valores de previsão corretos foram considerados corretos pelo nosso classificador. A precisão dividirá esses valores positivos verdadeiros por qualquer coisa que foi prevista como positiva:

$$
precisão = frac{texto{verdadeiro positivo}}{texto{verdadeiro positivo} + texto{falso positivo}}
$$

  1. Recordar: para entender quantos dos verdadeiros positivos foram identificados pelo nosso classificador. O recall é calculado dividindo os verdadeiros positivos por qualquer coisa que deveria ter sido prevista como positiva:

$$
recall = frac{texto{verdadeiro positivo}}{texto{verdadeiro positivo} + texto{falso negativo}}
$$

  1. Pontuação F1: é o balanceado ou média harmônica de precisão e revocação. O valor mais baixo é 0 e o mais alto é 1. Quando f1-score é igual a 1, significa que todas as classes foram previstas corretamente – esta é uma pontuação muito difícil de obter com dados reais:

$$
text{f1-score} = 2* frac{text{precisão} * text{recall}}{text{precisão} + text{recall}}
$$

  1. Precisão: descreve quantas previsões nosso classificador acertou. O valor de precisão mais baixo é 0 e o mais alto é 1. Esse valor geralmente é multiplicado por 100 para obter uma porcentagem:

$$
precisão = frac{text{número de previsões corretas}}{text{número total de previsões}}
$$

Observação: É extremamente difícil obter 100% de precisão em qualquer dado real, se isso acontecer, esteja ciente de que algum vazamento ou algo errado pode estar acontecendo - não há consenso sobre um valor de precisão ideal e também depende do contexto. Um valor de 70%, o que significa que o classificador cometerá erros em 30% dos dados, ou acima de 70% tende a ser suficiente para a maioria dos modelos.

from sklearn.metrics import classification_report
cr = classification_report(y_test, y_pred)
print(cr)

Podemos então olhar para a saída do relatório de classificação:

				precision    recall  f1-score   support

           0       0.83      0.91      0.87       316
           1       0.90      0.81      0.85       309

    accuracy                           0.86       625
   macro avg       0.86      0.86      0.86       625
weighted avg       0.86      0.86      0.86       625

Este é o nosso resultado. Notar que precision, recall, f1-score e accuracy as métricas são todas muito altas, acima de 80%, o que é ideal – mas esses resultados provavelmente foram influenciados por altas correlações e não se sustentarão no longo prazo.

A precisão do modelo é de 86%, o que significa que a classificação está errada em 14% das vezes. Temos essa informação geral, mas seria interessante saber se os 14% de erros acontecem em relação à classificação de classe 0 ou classe 1. Para identificar quais classes são erroneamente identificadas como quais e em qual frequência – podemos calcular e plotar um matriz de confusão das previsões do nosso modelo.

Avaliando o modelo com uma matriz de confusão

Vamos calcular e então traçar a matriz de confusão. Depois de fazer isso, podemos entender cada parte dela. Para traçar a matriz de confusão, usaremos o Scikit-Learn confusion_matrix(), que importaremos do metrics módulo.

A matriz de confusão é mais fácil de visualizar usando um Seaborn heatmap(). Assim, após gerá-lo, passaremos nossa matriz de confusão como argumento para o mapa de calor:

from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt='d')

Guia definitivo para regressão logística em Python PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

  1. Matriz de Confusão: a matriz mostra quantas amostras o modelo acertou ou errou para cada classe. Os valores que foram corretos e corretamente previstos são chamados verdadeiros positivos, e aqueles que foram previstos como positivos, mas não foram positivos, são chamados falso-positivo. A mesma nomenclatura de verdadeiros negativos e falsos negativos é usado para valores negativos;

Observando o gráfico da matriz de confusão, podemos ver que temos 287 valores que foram 0 e previsto como 0 - ou verdadeiros positivos para classe 0 (as sementes Çerçevelik). Nos tambem temos 250 verdadeiros positivos para a classe 1 (sementes de Ürgüp Sivrisi). Os verdadeiros positivos estão sempre localizados na diagonal da matriz que vai do canto superior esquerdo ao inferior direito.

Temos também 29 valores que deveriam ser 0, mas previsto como 1 (falso-positivo) e 59 valores que foram 1 e previsto como 0 (falsos negativos). Com esses números, podemos entender que o erro que o modelo mais comete é que ele prevê falsos negativos. Portanto, pode acabar classificando uma semente de Ürgüp Sivrisi como uma semente de Çerçevelik.

Esse tipo de erro também é explicado pelos 81% de recordação da aula 1. Observe que as métricas estão conectadas. E a diferença no recall vem de ter 100 amostras a menos da classe Ürgüp Sivrisi. Esta é uma das implicações de ter apenas algumas amostras a menos do que a outra classe. Para melhorar ainda mais a recuperação, você pode experimentar com pesos de classe ou usar mais amostras Ürgüp Sivrisi.

Até agora, executamos a maioria das etapas tradicionais da ciência de dados e usamos o modelo de regressão logística como uma caixa preta.

Observação: Se você quiser ir mais longe, use Validação cruzada (CV) e pesquisa de grade procurar, respectivamente, o modelo que mais generaliza em relação aos dados e os melhores parâmetros do modelo escolhidos antes do treinamento, ou hiperparâmetros.

Idealmente, com CV e Grid Search, você também pode implementar uma maneira concatenada de fazer etapas de pré-processamento de dados, divisão de dados, modelagem e avaliação - o que é facilitado com o Scikit-Learn dutos.

Agora é hora de abrir a caixa preta e olhar dentro dela, para entender melhor como funciona a regressão logística.

Aprofundando como a regressão logística realmente funciona

A regressão palavra não está lá por acaso, para entender o que a regressão logística faz, podemos lembrar o que sua irmã, a regressão linear, faz com os dados. A fórmula de regressão linear foi a seguinte:

$$
y = b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n
$$

Em que b0 foi o intercepto de regressão, b1 o coeficiente e x1 os dados.

Essa equação resultou em uma linha reta que foi usada para prever novos valores. Relembrando a introdução, a diferença agora é que não vamos prever novos valores, mas uma classe. Então essa linha reta precisa mudar. Com a regressão logística, introduzimos uma não linearidade e a previsão agora é feita usando uma curva em vez de uma linha:

Guia definitivo para regressão logística em Python PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Observe que enquanto a linha de regressão linear continua e é feita de valores infinitos contínuos, a curva de regressão logística pode ser dividida ao meio e tem extremos em valores 0 e 1. Essa forma de “S” é a razão pela qual classifica os dados – os pontos que estão mais próximos ou caem na extremidade mais alta pertencem à classe 1, enquanto os pontos que estão no quadrante inferior ou mais próximos de 0 pertencem à classe 0. o “S” é o meio entre 0 e 1, 0.5 – é o limite para os pontos de regressão logística.

Guia definitivo para regressão logística em Python PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Já entendemos a diferença visual entre regressão logística e linear, mas e a fórmula? A fórmula da regressão logística é a seguinte:

$$
y = b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n
$$

Também pode ser escrito como:

$$
y_{prob} = frac{1}{1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}
$$

Ou ainda ser escrito como:

$$
y_{prob} = frac{e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}{1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}
$$

Na equação acima, temos a probabilidade de entrada, em vez de seu valor. Tem 1 como seu numerador para que possa resultar em um valor entre 0 e 1, e 1 mais um valor em seu denominador, de modo que seu valor seja 1 e algo - isso significa que o resultado da fração inteira não pode ser maior que 1 .

E qual é o valor que está no denominador? Isso é e, a base do logaritmo natural (aproximadamente 2.718282), elevada à potência da regressão linear:

$$
e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

Outra forma de escrever seria:

$$
ln esquerda( frac{p}{1-p} direita) = {(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

Nessa última equação, ln é o logaritmo natural (base e) e p é a probabilidade, então o logaritmo da probabilidade do resultado é o mesmo que o resultado da regressão linear.

Em outras palavras, com o resultado da regressão linear e o logaritmo natural, podemos chegar à probabilidade de uma entrada pertencer ou não a uma classe projetada.

Todo o processo de derivação da regressão logística é o seguinte:

$$
p{X} = frac{e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}{1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}
$$

$$
p(1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}) = e^{(b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

$$
p + p*e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)} = e^{(b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

p
=

e

(

b
0

+

b
1

*

x
1

+

b
2

*

x
2

+

b
3

*

x
3

+
...
+

b
n

*

x
n

)

-
p
*

e

(

b
0

+

b
1

*

x
1

+

b
2

*

x
2

+

b
3

*

x
3

+
...
+

b
n

*

x
n

)

$$
frac{p}{1-p} = e^{(b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)}
$$

$$
ln esquerda( frac{p}{1-p} direita) = (b_0 + b_1 * x_1 + b_2 *x_2 + b_3 * x_3 + ldots + b_n * x_n)
$$

Isso significa que o modelo de regressão logística também possui coeficientes e um valor de intercepto. Porque ele usa uma regressão linear e adiciona um componente não linear a ela com o logaritmo natural (e).

Podemos ver os valores dos coeficientes e intercepto do nosso modelo, da mesma forma que fizemos para a regressão linear, usando coef_ e intercept_ propriedades:

logreg.coef_

Que exibe os coeficientes de cada uma das 12 características:

array([[ 1.43726172, -1.03136968,  0.24099522, -0.61180768,  1.36538261,
        -1.45321951, -1.22826034,  0.98766966,  0.0438686 , -0.78687889,
         1.9601197 , -1.77226097]])
logreg.intercept_

Isso resulta em:

array([0.08735782])

Com os coeficientes e valores de interceptação, podemos calcular as probabilidades previstas de nossos dados. Vamos pegar o primeiro X_test valores novamente, como exemplo:

X_test[:1]

Isso retorna a primeira linha de X_test como uma matriz NumPy:

array([[-1.09830823, -0.93651823, -0.60794138, -1.13255059, -1.0827684 ,
        -1.12235877,  0.45891056, -1.07825898,  0.56284738, -0.17604099,
         0.23661678, -0.36013424]])

Seguindo a equação inicial:

$$
p{X} = frac{e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}{1 + e^{(b_0 + b_1 * x_1 + b_2 * x_2 + b_3 * x_3 + ldots + b_n * x_n)}}
$$

Em python, temos:

import math

lin_reg = logreg.intercept_[0] + 
((logreg.coef_[0][0]* X_test[:1][0][0])+ 
(logreg.coef_[0][1]* X_test[:1][0][1])+ 
(logreg.coef_[0][2]* X_test[:1][0][2])+ 
(logreg.coef_[0][3]* X_test[:1][0][3])+ 
(logreg.coef_[0][4]* X_test[:1][0][4])+ 
(logreg.coef_[0][5]* X_test[:1][0][5])+ 
(logreg.coef_[0][6]* X_test[:1][0][6])+ 
(logreg.coef_[0][7]* X_test[:1][0][7])+ 
(logreg.coef_[0][8]* X_test[:1][0][8])+ 
(logreg.coef_[0][9]* X_test[:1][0][9])+ 
(logreg.coef_[0][10]* X_test[:1][0][10])+ 
(logreg.coef_[0][11]* X_test[:1][0][11]))

px = math.exp(lin_reg)/(1 +(math.exp(lin_reg)))
px

Isto resulta em:

0.45273372469369133

Se olharmos novamente para o predict_proba resultado do primeiro X_test linha, temos:

logreg.predict_proba(X_test[:1])


Isso significa que a equação de regressão logística original nos dá a probabilidade da entrada em relação à classe 1, para descobrir qual probabilidade é para a classe 0, podemos simplesmente:

1 - px


Observe que ambos px e 1-px são idênticos a predict_proba resultados. É assim que a regressão logística é calculada e por que regressão faz parte do seu nome. Mas e o termo logística?

O termo logística vem de logar, que é uma função que já vimos:

$$
ln esquerda( frac{p}{1-p} direita)
$$

Acabamos de calcular com px e 1-px. Este é o logit, também chamado probabilidades de log uma vez que é igual ao logaritmo das probabilidades onde p é uma probabilidade.

Conclusão

Neste guia, estudamos um dos algoritmos de classificação de aprendizado de máquina mais fundamentais, ou seja, regressão logística.

Inicialmente, implementamos a regressão logística como uma caixa preta com a biblioteca de aprendizado de máquina do Scikit-Learn e, posteriormente, entendemos passo a passo para ter um motivo claro e de onde vêm os termos regressão e logística.

Também exploramos e estudamos os dados, entendendo que é uma das partes mais cruciais de uma análise de ciência de dados.

A partir daqui, eu aconselho você a brincar com regressão logística multiclasse, regressão logística para mais de duas classes – você pode aplicar o mesmo algoritmo de regressão logística para outros conjuntos de dados que tenham várias classes e interpretar os resultados.

Observação: Uma boa coleção de conjuntos de dados está disponível SUA PARTICIPAÇÃO FAZ A DIFERENÇA para você brincar.

Eu também aconselho você a estudar a L1 e L2 regularizações, são uma forma de “penalizar” os dados mais altos para que se aproximem do normal, mantendo a complexidade do modelo, para que o algoritmo chegue a um resultado melhor. A implementação Scikit-Learn que usamos, já possui regularização L2 por padrão. Outra coisa a se observar é a diferença solucionadores, como lbgs, que otimizam o desempenho do algoritmo de regressão logística.

Também é importante dar uma olhada na estatístico abordagem da regressão logística. Tem suposições sobre o comportamento dos dados e sobre outras estatísticas que devem ser mantidas para garantir resultados satisfatórios, tais como:

  • as observações são independentes;
  • não há multicolinearidade entre as variáveis ​​explicativas;
  • não há outliers extremos;
  • existe uma relação linear entre as variáveis ​​explicativas e o logit da variável resposta;
  • o tamanho da amostra é suficientemente grande.

Observe quantas dessas suposições já foram abordadas em nossa análise e tratamento de dados.

Espero que você continue explorando o que a regressão logística tem a oferecer em todas as suas diferentes abordagens!

Carimbo de hora:

Mais de Abuso de pilha