Guía definitiva de regresión logística en Python PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Guía definitiva de regresión logística en Python

Introducción

A veces se confunde con regresión lineal por novatos – debido a compartir el término regresiónregresión logística es muy diferente de regresión lineal. Mientras que la regresión lineal predice valores como 2, 2.45, 6.77 o valores continuoshaciéndolo un regresión algoritmo, regresión logística predice valores como 0 o 1, 1 o 2 o 3, que son valores discretoshaciéndolo un clasificación algoritmo. si, se llama regresión pero es un clasificación algoritmo. Más sobre esto en un momento.

Por lo tanto, si su problema de ciencia de datos involucra valores continuos, puede aplicar un regresión algoritmo (la regresión lineal es uno de ellos). De lo contrario, si se trata de clasificar entradas, valores discretos o clases, puede aplicar un clasificación algoritmo (la regresión logística es uno de ellos).

En esta guía, realizaremos una regresión logística en Python con la biblioteca Scikit-Learn. También explicaremos por qué la palabra "regresión" está presente en el nombre y cómo funciona la regresión logística.

Para ello, primero cargaremos datos que serán clasificados, visualizados y preprocesados. Luego, construiremos un modelo de regresión logística que comprenderá esos datos. Luego, este modelo se evaluará y se empleará para predecir valores en función de la nueva entrada.

Motivación

La empresa para la que trabaja se asoció con una granja agrícola turca. Esta asociación implica la venta de semillas de calabaza. Las semillas de calabaza son muy importantes para la nutrición humana. Contienen una buena proporción de carbohidratos, grasas, proteínas, calcio, potasio, fósforo, magnesio, hierro y zinc.

En el equipo de ciencia de datos, su tarea es diferenciar entre los tipos de semillas de calabaza simplemente usando datos, o clasificando los datos según el tipo de semilla.

La granja turca trabaja con dos tipos de semillas de calabaza, una se llama Çerçevelik y el otro Ürgüp Sivrisi.

Para clasificar las semillas de calabaza, su equipo ha seguido el documento de 2021 “El uso de métodos de aprendizaje automático en la clasificación de semillas de calabaza (Cucurbita pepo L.). Recursos genéticos y evolución de cultivos” de Koklu, Sarigil y Ozbek: en este documento, hay una metodología para fotografiar y extraer las medidas de las semillas de las imágenes.

Después de completar el proceso descrito en el documento, se extrajeron las siguientes medidas:

  • Área – el número de píxeles dentro de los bordes de una semilla de calabaza
  • Perímetro – la circunferencia en píxeles de una semilla de calabaza
  • Longitud del eje principal – también la circunferencia en píxeles de una semilla de calabaza
  • Longitud del eje menor – la pequeña distancia del eje de una semilla de calabaza
  • Excentricidad – la excentricidad de una semilla de calabaza
  • área convexa – el número de píxeles de la capa convexa más pequeña en la región formada por la semilla de calabaza
  • Grado – la relación entre el área de una semilla de calabaza y los píxeles del cuadro delimitador
  • Diámetro equivalente – la raíz cuadrada de la multiplicación del área de la semilla de calabaza por cuatro dividida por pi
  • Compacidad – la proporción del área de la semilla de calabaza con respecto al área del círculo con la misma circunferencia
  • Solidez – la condición convexa y convexa de las semillas de calabaza
  • Redondez – la ovalidad de las semillas de calabaza sin considerar las distorsiones de sus bordes
  • Relación de aspecto – la relación de aspecto de las semillas de calabaza

Esas son las medidas con las que tienes que trabajar. Además de las medidas, también está el Clase etiqueta para los dos tipos de semillas de calabaza.

Para comenzar a clasificar las semillas, importemos los datos y comencemos a verlos.

Comprender el conjunto de datos

Nota: Puede descargar el conjunto de datos de calabaza esta página.

Después de descargar el conjunto de datos, podemos cargarlo en una estructura de marco de datos usando el pandas biblioteca. Dado que es un archivo de Excel, usaremos el read_excel() método:

import pandas as pd

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

Una vez que se cargan los datos, podemos echar un vistazo rápido a las primeras 5 filas usando el head() método:

df.head() 

Esto resulta en:

	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

Aquí tenemos todas las medidas en sus respectivas columnas, nuestra Características, y también la Clase columna, nuestra dirigidos, que es el último en el marco de datos. Podemos ver cuantas medidas tenemos usando el shape atributo:

df.shape 

El resultado es:

(2500, 13)

El resultado de la forma nos dice que hay 2500 entradas (o filas) en el conjunto de datos y 13 columnas. Como sabemos que hay una columna de destino, esto significa que tenemos 12 columnas de características.

Ahora podemos explorar la variable objetivo, la semilla de calabaza. Class. Como vamos a predecir esa variable, es interesante ver cuántas muestras de cada semilla de calabaza tenemos. Por lo general, cuanto menor sea la diferencia entre el número de instancias en nuestras clases, más equilibrada será nuestra muestra y mejores serán nuestras predicciones.

Esta inspección se puede hacer contando cada muestra de semilla con el value_counts() método:

df['Class'].value_counts() 

El código anterior muestra:

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

Podemos ver que hay 1300 muestras del Çerçevelik semilla y 1200 muestras de la Ürgüp Sivrisi semilla. Note que la diferencia entre ellos es de 100 muestras, una diferencia muy pequeña, lo cual es bueno para nosotros e indica que no hay necesidad de reequilibrar el número de muestras.

Veamos también las estadísticas descriptivas de nuestras características con el describe() para ver qué tan bien distribuidos están los datos. También transpondremos la tabla resultante con T para facilitar la comparación entre estadísticas:

df.describe().T

La tabla resultante es:

					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

Mirando la tabla, al comparar los personalizado y desviación estándar (std) columnas, se puede ver que la mayoría de las características tienen una media que está lejos de la desviación estándar. Eso indica que los valores de los datos no están concentrados alrededor del valor medio, sino más dispersos alrededor de él; en otras palabras, tienen alta variabilidad.

Además, al mirar el mínimo (min) y máximas (max) columnas, algunas características, como Areay Convex_Area, tienen grandes diferencias entre los valores mínimo y máximo. Esto significa que esas columnas tienen datos muy pequeños y también valores de datos muy grandes, o mayor amplitud entre valores de datos.

Con alta variabilidad, alta amplitud y características con diferentes unidades de medida, la mayoría de nuestros datos se beneficiarían de tener la misma escala para todas las características o de ser escamoso. El escalado de datos centrará los datos en torno a la media y reducirá su varianza.

Este escenario probablemente también indica que hay valores atípicos y extremos en los datos. Por lo tanto, lo mejor es tener algunos tratamiento atípico además de escalar los datos.

Hay algunos algoritmos de aprendizaje automático, por ejemplo, algoritmos basados ​​en árboles como Clasificación aleatoria de bosques, que no se ven afectados por una gran varianza de datos, valores atípicos y valores extremos. Regresión logística es diferente, se basa en una función que categoriza nuestros valores, y los parámetros de esa función pueden verse afectados por valores que están fuera de la tendencia general de los datos y tienen una varianza alta.

Entenderemos más sobre la regresión logística en un momento cuando lleguemos a implementarla. Por ahora, podemos seguir explorando nuestros datos.

Nota: Hay un dicho popular en Informática: “Basura entra, basura sale” (GIGO), que es muy adecuado para el aprendizaje automático. Esto significa que cuando tenemos datos basura (medidas que no describen los fenómenos en sí mismos, datos que no se entendieron y no se prepararon bien de acuerdo con el tipo de algoritmo o modelo), probablemente generarán una salida incorrecta que no funcionará. un día a día.
Esta es una de las razones por las que es tan importante explorar, comprender los datos y cómo funciona el modelo elegido. Al hacer eso, podemos evitar poner basura en nuestro modelo, poniendo valor en él y obteniendo valor.

Visualización de los datos

Hasta ahora, con las estadísticas descriptivas, tenemos una instantánea algo abstracta de algunas cualidades de los datos. Otro paso importante es visualizarlo y confirmar nuestra hipótesis de alta varianza, amplitud y valores atípicos. Para ver si lo que hemos observado hasta ahora se muestra en los datos, podemos trazar algunos gráficos.

También es interesante ver cómo se relacionan las características con las dos clases que se pronosticarán. Para hacer eso, vamos a importar el seaborn paquete y use el pairplot gráfico para ver cada distribución de características y cada separación de clase por característica:

import seaborn as sns


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

Nota: El código anterior puede tardar un poco en ejecutarse, ya que el diagrama de pares combina diagramas de dispersión de todas las características (puede) y también muestra las distribuciones de características.

Mirando el gráfico de pares, podemos ver que en la mayoría de los casos los puntos de la Çerçevelik clase están claramente separados de los puntos de la Ürgüp Sivrisi clase. O los puntos de una clase están a la derecha cuando los otros están a la izquierda, o algunos están arriba mientras que otros están abajo. Si tuviéramos que usar algún tipo de curva o línea para separar las clases, esto demuestra que es más fácil separarlas, si estuvieran mezcladas, la clasificación sería una tarea más difícil.

En Eccentricity, Compactness y Aspect_Ration columnas, algunos puntos que están "aislados" o que se desvían de la tendencia general de los datos (valores atípicos) también se detectan fácilmente.

Al mirar la diagonal desde la parte superior izquierda hasta la parte inferior derecha del gráfico, observe que las distribuciones de datos también están codificadas por colores de acuerdo con nuestras clases. Las formas de distribución y la distancia entre ambas curvas son otros indicadores de cuán separables son, cuanto más lejos, mejor. En la mayoría de los casos, no se superponen, lo que implica que son más fáciles de separar, contribuyendo también a nuestra tarea.

En secuencia, también podemos trazar los diagramas de caja de todas las variables con el sns.boxplot() método. La mayoría de las veces, es útil orientar los diagramas de caja horizontalmente, para que las formas de los diagramas de caja sean las mismas que las formas de distribución, podemos hacerlo con el orient argumento:


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

Guía definitiva de regresión logística en Python PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

En el gráfico anterior, observe que Area y Convex_Area tienen una magnitud tan alta en comparación con las magnitudes de las otras columnas, que aplastan las otras gráficas de caja. Para poder ver todos los diagramas de caja, podemos escalar las características y trazarlas nuevamente.

Antes de hacer eso, entendamos que si hay valores de características que están íntimamente relacionados con otros valores, por ejemplo, si hay valores que también aumentan cuando otros valores de características aumentan, teniendo un correlación positiva; o si hay valores que hacen lo contrario, se hacen más pequeños mientras otros valores se hacen más pequeños, teniendo una correlación negativa.

Es importante tener en cuenta esto porque tener relaciones sólidas en los datos podría significar que algunas columnas se derivaron de otras columnas o que tienen un significado similar para nuestro modelo. Cuando eso sucede, los resultados del modelo pueden estar sobreestimados y queremos resultados más cercanos a la realidad. Si hay correlaciones fuertes, también significa que podemos reducir la cantidad de características y usar menos columnas, lo que hace que el modelo sea más parsimonioso.

Nota: La correlación por defecto calculada con el corr() el método es el Coeficiente de correlación de Pearson. Este coeficiente se indica cuando los datos son cuantitativos, normalmente distribuidos, no tienen valores atípicos y tienen una relación lineal.

Otra opción sería calcular Coeficiente de correlación de Spearman. El coeficiente de Spearman se usa cuando los datos son ordinales, no lineales, tienen cualquier distribución y tienen valores atípicos. Tenga en cuenta que nuestros datos no se ajustan totalmente a las suposiciones de Pearson o Spearman (también hay más métodos de correlación, como el de Kendall). Dado que nuestros datos son cuantitativos y es importante para nosotros medir su relación lineal, utilizaremos el coeficiente de Pearson.

Echemos un vistazo a las correlaciones entre las variables y luego podemos pasar a preprocesar los datos. Calcularemos las correlaciones con el corr() método y visualizarlos con Seaborn's heatmap(). El tamaño estándar del mapa de calor tiende a ser pequeño, por lo que importaremos matplotlib (motor/biblioteca de visualización general sobre la que está construido Seaborn) y cambie el tamaño con figsize:

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

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

Guía definitiva de regresión logística en Python PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

En este mapa de calor, los valores más cercanos a 1 o -1 son los valores a los que debemos prestar atención. El primer caso, denota una alta correlación positiva y el segundo, una alta correlación negativa. Ambos valores, si no superan 0.8 o -0.8, serán beneficiosos para nuestro modelo de regresión logística.

Cuando existen correlaciones altas como la de 0.99 entre Aspec_Ration y Compactness, esto significa que podemos elegir usar solo Aspec_Ration o solo Compactness, en lugar de ambos (ya que casi serían iguales predictores el uno del otro). Lo mismo vale para Eccentricity y Compactness con un -0.98 correlación, por Area y Perimeter con un 0.94 correlación y algunas otras columnas.

Preprocesamiento de los datos

Como ya hemos explorado los datos durante un tiempo, podemos comenzar a procesarlos previamente. Por ahora, usemos todas las funciones para la predicción de clase. Después de obtener un primer modelo, una línea base, podemos eliminar algunas de las columnas altamente correlacionadas y compararlas con la línea base.

Las columnas de características serán nuestras X datos y la columna de clase, nuestra y datos objetivo:

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

Convertir características categóricas en características numéricas

En cuanto a nuestro Class columna: sus valores no son números, esto significa que también debemos transformarlos. Hay muchas formas de hacer esta transformación; aquí, usaremos el replace() método y reemplazar Çerçevelik a 0 y Ürgüp Sivrisi a 1.

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

¡Tenga en cuenta el mapeo! Cuando lea los resultados de su modelo, querrá volver a convertirlos al menos en su mente, o volver a convertirlos en el nombre de clase para otros usuarios.

División de datos en conjuntos de entrenamiento y prueba

En nuestra exploración, notamos que las características necesitaban escalarse. Si hiciéramos la escala ahora, o de forma automática, escalaríamos los valores con la totalidad de X y y. En ese caso, introduciríamos fuga de datos, ya que los valores del futuro conjunto de prueba habrían afectado la escala. La fuga de datos es una causa común de resultados irreproducibles y un alto rendimiento ilusorio de los modelos de ML.

Pensar en la escala muestra que primero debemos dividir X y y datos más en el tren y los conjuntos de prueba y luego a cómodo un escalador en el conjunto de entrenamiento, y para transformar tanto el tren como los conjuntos de prueba (sin que el conjunto de prueba impacte en el escalador que hace esto). Para ello, utilizaremos Scikit-Learn's 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)

Fijar test_size=.25 es garantizar que estemos utilizando el 25 % de los datos para pruebas y el 75 % para capacitación. Esto podría omitirse, una vez que sea la división predeterminada, pero el Pitónico forma de escribir código aconseja que ser "explícito es mejor que implícito".

Nota: La oración "explícito es mejor que implícito" es una referencia a El zen de pitóno PEP20. Presenta algunas sugerencias para escribir código Python. Si se siguen esas sugerencias, el código se considera Pitónico. Puedes saber más al respecto esta página.

Después de dividir los datos en conjuntos de prueba y entrenamiento, es una buena práctica observar cuántos registros hay en cada conjunto. Eso se puede hacer con el shape atributo:

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

Esto muestra:

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

Podemos ver que después de la división, tenemos 1875 registros para entrenamiento y 625 para pruebas.

Datos de escala

Una vez que tenemos listos nuestros conjuntos de entrenamiento y prueba, podemos proceder a escalar los datos con Scikit-Learn StandardScaler objeto (u otros escaladores proporcionados por la biblioteca). Para evitar fugas, el raspador se instala en el X_train Los datos y los valores del tren se utilizan luego para escalar, o transformar, tanto el tren como los datos de prueba:

from sklearn.preprocessing import StandardScaler

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

Ya que normalmente llamarás:

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

Las dos primeras líneas se pueden colapsar con un singular fit_transform() call, que encaja el scaler en el plató, y lo transforma de una vez. Ahora podemos reproducir los gráficos de diagramas de caja para ver la diferencia después de escalar los datos.

Teniendo en cuenta que la escala elimina los nombres de las columnas, antes de trazar, podemos organizar los datos del tren en un marco de datos con los nombres de las columnas nuevamente para facilitar la visualización:

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

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

Guía definitiva de regresión logística en Python PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

¡Finalmente podemos ver todos nuestros diagramas de caja! Observe que todos ellos tienen valores atípicos y las características que presentan una distribución más alejada de la normal (que tienen curvas sesgadas hacia la izquierda o hacia la derecha), como Solidity, Extent, Aspect_Rationy Compactedness, son los mismos que tuvieron mayores correlaciones.

Eliminación de valores atípicos con el método IQR

Ya sabemos que la regresión logística puede verse afectada por valores atípicos. Una de las formas de tratarlos es usar un método llamado Rango intercuartil or RIC. El paso inicial del método IQR es dividir los datos de nuestro tren en cuatro partes, llamadas cuartiles. El primer cuartil, Q1, asciende al 25% de los datos, el segundo, Q2, al 50%, el tercero, Q3, al 75%, y el último, Q4, al 100%. Las cajas en el diagrama de caja están definidas por el método IQR y son una representación visual del mismo.

Considerando un diagrama de caja horizontal, la línea vertical de la izquierda marca el 25% de los datos, la línea vertical del medio, el 50% de los datos (o la mediana), y la última línea vertical de la derecha, el 75% de los datos. . Cuanto más uniforme sea el tamaño de ambos cuadrados definidos por las líneas verticales, o cuanto más en el medio esté la línea vertical mediana, significa que nuestros datos están más cerca de la distribución normal o menos sesgados, lo que es útil para nuestro análisis.

Además de la caja IQR, también hay líneas horizontales a ambos lados de la misma. Esas líneas marcan los valores mínimos y máximos de distribución definidos por

$$
Mínimo = Q1 – 1.5*RIQ
$$

y

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

IQR es exactamente la diferencia entre Q3 y Q1 (o Q3 – Q1) y es el punto de datos más central. Por eso, al encontrar el IQR, terminamos filtrando los valores atípicos en los extremos de los datos, o en los puntos mínimo y máximo. Los diagramas de caja nos dan un adelanto de cuál será el resultado del método IQR.

Guía definitiva de regresión logística en Python PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Podemos usar Pandas quantile() método para encontrar nuestros cuantiles, y iqr del desplegable scipy.stats paquete para obtener el rango de datos intercuartiles para cada columna:

from scipy.stats import iqr

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

IQR = X_train.apply(iqr)

Ahora que tenemos Q1, Q3 e IQR, podemos filtrar los valores más cercanos a la 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]

Después de filtrar nuestras filas de entrenamiento, podemos ver cuántas de ellas todavía están en los datos con shape:

X_train.shape

Esto resulta en:

(1714, 12)

Podemos ver que el número de filas pasó de 1875 a 1714 después del filtrado. Esto significa que 161 filas contenían valores atípicos o el 8.5 % de los datos.

Nota: Se recomienda que el filtrado de valores atípicos, la eliminación de valores de NaN y otras acciones que implican el filtrado y la limpieza de datos permanezcan por debajo o hasta el 10 % de los datos. Intente pensar en otras soluciones si su filtrado o eliminación supera el 10% de sus datos.

Después de eliminar los valores atípicos, estamos casi listos para incluir datos en el modelo. Para el ajuste del modelo, utilizaremos datos de trenes. X_train se filtra, pero ¿qué pasa con y_train?

y_train.shape

Esto produce:

(1875,)

Darse cuenta de y_train todavía tiene 1875 filas. Necesitamos igualar el número de y_train filas al número de X_train filas y no solo arbitrariamente. Necesitamos eliminar los valores de y de las instancias de semillas de calabaza que eliminamos, que probablemente estén dispersas por el y_train establecer. el filtrado X_train todavía tiene sus índices originales y el índice tiene espacios en los que eliminamos los valores atípicos. Entonces podemos usar el índice de la X_train DataFrame para buscar los valores correspondientes en y_train:

y_train = y_train.iloc[X_train.index]

Después de hacer eso, podemos mirar el y_train forma de nuevo:

y_train.shape

Qué salidas:

(1714,)

Consulte nuestra guía práctica y práctica para aprender Git, con las mejores prácticas, los estándares aceptados por la industria y la hoja de trucos incluida. Deja de buscar en Google los comandos de Git y, de hecho, aprenden ella!

Ahora, y_train también tiene 1714 filas y son las mismas que las X_train filas ¡Finalmente estamos listos para crear nuestro modelo de regresión logística!

Implementación del modelo de regresión logística

¡La parte más difícil ya está hecha! El preprocesamiento suele ser más difícil que el desarrollo de modelos, cuando se trata de usar bibliotecas como Scikit-Learn, que han simplificado la aplicación de modelos ML a solo un par de líneas.

Primero, importamos el LogisticRegression clase e instanciarlo, creando un LogisticRegression :

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

En segundo lugar, ajustamos los datos de nuestro tren a la logreg modelo con el fit() método, y predecir nuestros datos de prueba con el predict() método, almacenando los resultados como y_pred:



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

¡Ya hemos hecho predicciones con nuestro modelo! Veamos las primeras 3 filas en X_train para ver qué datos hemos utilizado:

X_train[:3]

El código anterior genera:

       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

Y en las primeras 3 predicciones en y_pred para ver los resultados:

y_pred[:3] 

Esto resulta en:

array([0, 0, 0])

Para esas tres filas, nuestras predicciones fueron que eran semillas de primera clase, Çerçevelik.

Con regresión logística, en lugar de predecir la clase final, como 0, también podemos predecir la probabilidad que tiene la fila de pertenecer a la 0 clase. Esto es lo que realmente sucede cuando la regresión logística clasifica los datos y la predict() Luego, el método pasa esta predicción a través de un umbral para devolver una clase "dura". Para predecir la probabilidad de pertenecer a una clase, predict_proba() se usa:

y_pred_proba = logreg.predict_proba(X_test)

También echemos un vistazo a los primeros 3 valores de las predicciones de probabilidades y:

y_pred_proba[:3] 

Qué salidas:

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

Ahora, en lugar de tres ceros, tenemos una columna para cada clase. En la columna de la izquierda, comenzando con 0.54726628, son las probabilidades de los datos pertenecientes a la clase 0; y en la columna de la derecha, empezando por 0.45273372, son la probabilidad de que pertenezca a la clase 1.

Nota: Esta diferencia en la clasificación también se conoce como en las y suave predicción. La predicción dura encuadra la predicción en una clase, mientras que las predicciones blandas generan la probabilidades de la instancia perteneciente a una clase.

Hay más información sobre cómo se realizó la salida prevista. en realidad no fue 0, pero un 55% de probabilidad de clase 0, y un 45% de probabilidad de clase 1. Esto muestra cómo los primeros tres X_test puntos de datos, pertenecientes a la clase 0, son realmente claros solo con respecto al tercer punto de datos, con una probabilidad del 86%, y no tanto para los primeros dos puntos de datos.

Cuando se comunican resultados mediante métodos de aprendizaje automático, por lo general es mejor devolver una clase suave y la probabilidad asociada como la "confianza" de esa clasificación.

Hablaremos más sobre cómo se calcula eso cuando profundicemos en el modelo. En este momento, podemos continuar con el siguiente paso.

Evaluación del modelo con informes de clasificación

El tercer paso es ver cómo se comporta el modelo en los datos de prueba. Podemos importar Scikit-Learn classification_report() y pasar nuestro y_test y y_pred como argumentos. Después de eso, podemos imprimir su respuesta.

El informe de clasificación contiene las métricas de clasificación más utilizadas, como precisión, recordar, puntuación f1y la exactitud.

  1. Precisión: para entender qué valores de predicción correctos fueron considerados correctos por nuestro clasificador. La precisión dividirá esos valores positivos verdaderos por todo lo que se predijo como positivo:

$$
precision = frac{texto{verdadero positivo}}{texto{verdadero positivo} + texto{falso positivo}}
$$

  1. Recordar: para comprender cuántos de los verdaderos positivos fueron identificados por nuestro clasificador. El recuerdo se calcula dividiendo los verdaderos positivos por cualquier cosa que debería haber sido pronosticada como positiva:

$$
recordar = frac{texto{verdadero positivo}}{texto{verdadero positivo} + texto{falso negativo}}
$$

  1. Puntuación F1: es el equilibrado o Significado armonico de precisión y recuerdo. El valor más bajo es 0 y el más alto es 1. Cuando f1-score es igual a 1, significa que todas las clases se predijeron correctamente; esta es una puntuación muy difícil de obtener con datos reales:

$$
texto{f1-score} = 2* frac{texto{precisión} * texto{recordar}}{texto{precisión} + texto{recordar}}
$$

  1. Exactitud: describe cuántas predicciones acertó nuestro clasificador. El valor de precisión más bajo es 0 y el más alto es 1. Ese valor se suele multiplicar por 100 para obtener un porcentaje:

$$
precision = frac{texto{número de predicciones correctas}}{texto{número total de predicciones}}
$$

Nota: Es extremadamente difícil obtener una precisión del 100% en cualquier dato real, si eso sucede, tenga en cuenta que podría estar ocurriendo alguna fuga o algo incorrecto: no hay consenso sobre un valor de precisión ideal y también depende del contexto. Un valor del 70 %, lo que significa que el clasificador cometerá errores en el 30 % de los datos, o por encima del 70 %, tiende a ser suficiente para la mayoría de los modelos.

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

A continuación, podemos ver el resultado del informe de clasificación:

				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 es nuestro resultado. Darse cuenta de precision, recall, f1-scorey accuracy todas las métricas son muy altas, por encima del 80%, lo cual es ideal, pero esos resultados probablemente se vieron influenciados por altas correlaciones y no se mantendrán a largo plazo.

La precisión del modelo es del 86 %, lo que significa que se equivoca en la clasificación el 14 % de las veces. Tenemos esa información general, pero sería interesante saber si el 14% de errores ocurre con respecto a la clasificación de la clase. 0 o clase 1. Para identificar qué clases se identifican erróneamente como cuáles y con qué frecuencia, podemos calcular y trazar un matriz de confusión de las predicciones de nuestro modelo.

Evaluación del modelo con una matriz de confusión

Calculemos y luego tracemos la matriz de confusión. Después de hacer eso, podemos entender cada parte de ella. Para trazar la matriz de confusión, usaremos Scikit-Learn confusion_matrix(), que importaremos desde el metrics módulo.

La matriz de confusión es más fácil de visualizar usando un Seaborn heatmap(). Entonces, después de generarlo, pasaremos nuestra matriz de confusión como argumento para el mapa de calor:

from sklearn.metrics import confusion_matrix

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

Guía definitiva de regresión logística en Python PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

  1. Matriz de confusión: la matriz muestra cuántas muestras acertó o erró el modelo para cada clase. Los valores que fueron correctos y correctamente predichos se llaman verdaderos positivos, y los que se pronosticaron como positivos pero no lo fueron se denominan falsos positivos. La misma nomenclatura de verdaderos negativos y falsos negativos se utiliza para valores negativos;

Al observar el gráfico de la matriz de confusión, podemos ver que tenemos 287 valores que fueron 0 y predicho como 0 - o verdaderos positivos para clase 0 (las semillas de Çerçevelik). También tenemos 250 verdaderos positivos para la clase 1 (Semillas de Ürgüp Sivrisi). Los verdaderos positivos siempre se ubican en la matriz diagonal que va desde la esquina superior izquierda a la esquina inferior derecha.

También tenemos 29 valores que se suponía que eran 0, pero predicho como 1 (falsos positivos) y 59 valores que fueron 1 y predicho como 0 (falsos negativos). Con esos números, podemos entender que el error que más comete el modelo es que predice falsos negativos. Por lo tanto, en su mayoría puede terminar clasificando una semilla de Ürgüp Sivrisi como una semilla de Çerçevelik.

Este tipo de error también se explica por el 81% de recuerdo de clase. 1. Observe que las métricas están conectadas. Y la diferencia en el retiro proviene de tener 100 muestras menos de la clase Ürgüp Sivrisi. Esta es una de las implicaciones de tener solo unas pocas muestras menos que la otra clase. Para mejorar aún más la recuperación, puede experimentar con pesos de clase o usar más muestras de Ürgüp Sivrisi.

Hasta ahora, hemos ejecutado la mayoría de los pasos tradicionales de la ciencia de datos y hemos utilizado el modelo de regresión logística como una caja negra.

Nota: Si quieres ir más allá, utiliza Validación cruzada (CV) y búsqueda en cuadrícula buscar, respectivamente, el modelo que más generaliza respecto a los datos, y los mejores parámetros del modelo que se eligen antes del entrenamiento, o hiperparámetros.

Idealmente, con CV y ​​Grid Search, también podría implementar una forma concatenada de realizar pasos de preprocesamiento de datos, división de datos, modelado y evaluación, lo cual se simplifica con Scikit-Learn. tuberías.

Ahora es el momento de abrir la caja negra y mirar dentro, para profundizar en la comprensión de cómo funciona la regresión logística.

Profundizando en cómo funciona realmente la regresión logística

La regresión palabra no está allí por accidente, para entender lo que hace la regresión logística, podemos recordar lo que su hermano, la regresión lineal, hace con los datos. La fórmula de regresión lineal fue la siguiente:

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

en que b0 fue el intercepto de la regresión, b1 el coeficiente y x1 los datos.

Esa ecuación resultó en una línea recta que se usó para predecir nuevos valores. Recordando la introducción, la diferencia ahora es que no predeciremos nuevos valores, sino una clase. Así que esa línea recta necesita cambiar. Con la regresión logística, introducimos una no linealidad y la predicción ahora se realiza utilizando una curva en lugar de una línea:

Guía definitiva de regresión logística en Python PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Observe que mientras la línea de regresión lineal continúa y está compuesta por infinitos valores continuos, la curva de regresión logística se puede dividir en el medio y tiene extremos en valores 0 y 1. Esa forma de "S" es la razón por la que clasifica los datos: los puntos que están más cerca o caen en el extremo superior pertenecen a la clase 1, mientras que los puntos que están en el cuadrante inferior o más cerca de 0 pertenecen a la clase 0. El medio de la “S” es el medio entre 0 y 1, 0.5 – es el umbral para los puntos de regresión logística.

Guía definitiva de regresión logística en Python PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Ya entendemos la diferencia visual entre regresión logística y lineal, pero ¿qué pasa con la fórmula? La fórmula para la regresión logística es la siguiente:

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

También se puede escribir como:

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

O incluso escribirse como:

$$
y_{prob} = fracción{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)}}
$$

En la ecuación anterior, tenemos la probabilidad de entrada, en lugar de su valor. Tiene 1 como numerador, por lo que puede dar como resultado un valor entre 0 y 1, y 1 más un valor en su denominador, por lo que su valor es 1 y algo, esto significa que el resultado de la fracción entera no puede ser mayor que 1 .

¿Y cuál es el valor que está en el denominador? Está e, la base del logaritmo natural (aproximadamente 2.718282), elevado a la potencia de regresión lineal:

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

Otra forma de escribirlo sería:

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

En esa última ecuación, ln es el logaritmo natural (base e) y p es la probabilidad, por lo que el logaritmo de la probabilidad del resultado es el mismo que el resultado de la regresión lineal.

En otras palabras, con el resultado de la regresión lineal y el logaritmo natural, podemos llegar a la probabilidad de que una entrada pertenezca o no a una clase diseñada.

Todo el proceso de derivación de la regresión logística es el siguiente:

$$
p{X} = fracción{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

)

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

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

Esto significa que el modelo de regresión logística también tiene coeficientes y un valor de intersección. Porque usa una regresión lineal y le agrega un componente no lineal con el logaritmo natural (e).

Podemos ver los valores de los coeficientes y la intersección de nuestro modelo, de la misma manera que lo hicimos para la regresión lineal, usando coef_ y intercept_ propiedades:

logreg.coef_

Que muestra los coeficientes de cada una de las 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_

Eso da como resultado:

array([0.08735782])

Con los coeficientes y los valores de intercepción, podemos calcular las probabilidades previstas de nuestros datos. Consigamos el primero X_test valores de nuevo, como un ejemplo:

X_test[:1]

Esto devuelve la primera fila de X_test como una 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]])

Siguiendo la ecuación inicial:

$$
p{X} = fracción{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)}}
$$

En python tenemos:

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

Esto resulta en:

0.45273372469369133

Si volvemos a mirar el predict_proba resultado de la primera X_test línea, tenemos:

logreg.predict_proba(X_test[:1])


Esto significa que la ecuación de regresión logística original nos da la probabilidad de entrada con respecto a la clase 1, para averiguar qué probabilidad es para la clase 0, podemos simplemente:

1 - px


Note que ambos px y 1-px son idénticos a predict_proba resultados. Así es como se calcula la regresión logística y por qué regresión es parte de su nombre. Pero ¿qué pasa con el término logístico?

El término logístico viene de logit, que es una función que ya hemos visto:

$$
ln izquierda( frac{p}{1-p} derecha)
$$

Lo acabamos de calcular con px y 1-px. Este es el logit, también llamado registro de probabilidades ya que es igual al logaritmo de las probabilidades donde p es una probabilidad.

Conclusión

En esta guía, hemos estudiado uno de los algoritmos de clasificación de aprendizaje automático más fundamentales, es decir regresión logística.

Inicialmente, implementamos la regresión logística como una caja negra con la biblioteca de aprendizaje automático de Scikit-Learn, y luego la entendimos paso a paso para tener claro por qué y de dónde provienen los términos regresión y logística.

También hemos explorado y estudiado los datos, entendiendo que es una de las partes más cruciales de un análisis de ciencia de datos.

A partir de aquí, te aconsejo que juegues con regresión logística multiclase, regresión logística para más de dos clases: puede aplicar el mismo algoritmo de regresión logística para otros conjuntos de datos que tengan varias clases e interpretar los resultados.

Nota: Una buena colección de conjuntos de datos está disponible esta página para que juegues.

También te aconsejaría estudiar la L1 y la L2 regularizaciones, son una forma de “penalizar” los datos más altos para que se acerquen más a lo normal, manteniendo la complejidad del modelo, para que el algoritmo pueda llegar a un mejor resultado. La implementación de Scikit-Learn que usamos ya tiene regularización L2 por defecto. Otra cosa a tener en cuenta es la diferencia solucionadores, Tales como lbgs, que optimizan el rendimiento del algoritmo de regresión logística.

También es importante echar un vistazo a la estadístico enfoque de la regresión logística. Tiene supuestos sobre el comportamiento de los datos, y sobre otras estadísticas que deben contener para garantizar resultados satisfactorios, tales como:

  • las observaciones son independientes;
  • no hay multicolinealidad entre las variables explicativas;
  • no hay valores atípicos extremos;
  • existe una relación lineal entre las variables explicativas y el logit de la variable de respuesta;
  • el tamaño de la muestra es suficientemente grande.

Observe cuántos de esos supuestos ya estaban cubiertos en nuestro análisis y tratamiento de datos.

¡Espero que siga explorando lo que la regresión logística tiene para ofrecer en todos sus diferentes enfoques!

Sello de tiempo:

Mas de Abuso de pila