Regresión lineal en Python con Scikit-Learn

En esta guía detallada, aprenda la teoría y la práctica detrás de la regresión lineal (univariante) y lineal múltiple (multivariante) en Python con Scikit-Learn.

Si hubiera estudiado más tiempo, ¿mejorarían sus puntajes generales?

Una forma de responder a esta pregunta es tener datos sobre cuánto tiempo estuviste y qué puntaje obtuviste. Entonces podemos tratar de ver si hay un patrón en esos datos, y si en ese patrón, cuando sumas las horas, también termina sumando el porcentaje de puntajes.

Por ejemplo, supongamos que tiene un conjunto de datos de puntaje por hora, que contiene entradas como 1.5h y 87.5% de puntaje. También podría contener puntuaciones de 1,61 h, 2,32 h y 78 %, 97 %. El tipo de tipo de datos que puede tener cualquier valor intermedio (o cualquier nivel de 'granularidad') se conoce como datos continuos.

Otro escenario es que tiene un conjunto de datos de puntaje por hora que contiene calificaciones basadas en letras en lugar de calificaciones basadas en números, como A, B o C. Las calificaciones son valores claros que se pueden aislar, ya que no puede tener una A. .23, A++++++++++++ (y hasta el infinito) o A * e^12. El tipo de tipo de datos que no se puede particionar o definir de forma más granular se conoce como datos discretos.

En función de la modalidad (forma) de sus datos, para averiguar qué puntaje obtendría en función de su tiempo de estudio, realizará una regresión o una clasificación.

La regresión se realiza sobre datos continuos, mientras que la clasificación se realiza sobre datos discretos. La regresión puede ser cualquier cosa, desde predecir la edad de alguien, la casa de un precio o el valor de cualquier variable. La clasificación incluye predecir a qué clase pertenece algo (por ejemplo, si un tumor es benigno o maligno).

{.icon aria-hidden=“true”}

Nota: Predecir los precios de la vivienda y la presencia de un cáncer no es una tarea sencilla, y ambos suelen incluir relaciones no lineales. Las relaciones lineales son bastante simples de modelar, como verá en un momento.

Si desea aprender a través de proyectos prácticos del mundo real, guiados por ejemplos, consulte nuestra ["Predicción práctica del precio de la vivienda: aprendizaje automático en Python"](https://wikihtp.com/courses /hands-on-house-price-prediction-machine-learning-in-python/){target="_blank"} y nuestro grado de investigación ["Clasificación del cáncer de mama con aprendizaje profundo: Keras y Tensorflow"] (https://wikihtp.com/courses/breast-cancer-classification-with-keras-and-tensorflow-custom-cnns-effnet-resnet-xception/){target="_blank"}!

Tanto para la regresión como para la clasificación, usaremos datos para predecir etiquetas (término general para las variables objetivo). Las etiquetas pueden ser desde "B" (clase) para tareas de clasificación hasta 123 (número) para tareas de regresión. Porque también proporcionamos las etiquetas: estos son algoritmos de aprendizaje supervisado.

En esta guía para principiantes, realizaremos una regresión lineal en Python, utilizando la biblioteca Scikit-Learn. Pasaremos por una canalización de aprendizaje automático de extremo a extremo. Primero cargaremos los datos de los que aprenderemos y los visualizaremos, al mismo tiempo que realizamos Análisis exploratorio de datos. Luego, preprocesaremos los datos y construiremos modelos que se ajusten a ellos (como un guante). Luego, este modelo se evalúa y, si es favorable, se usa para predecir nuevos valores en función de la nueva entrada.

{.icon aria-hidden=“true”}

Nota: Puedes descargar el cuaderno que contiene todo el código en esta guía aquí.

Análisis de datos exploratorios {#análisis de datos exploratorios}

{.icon aria-hidden=“true”}

Nota: Puede descargar el conjunto de datos de puntuación de horas aquí.

Comencemos con el análisis exploratorio de datos. Primero desea conocer sus datos; esto incluye cargarlos, visualizar características, explorar sus relaciones y hacer hipótesis basadas en sus observaciones. El conjunto de datos es un archivo CSV (valores separados por comas), que contiene las horas estudiadas y los puntajes obtenidos en base a esas horas. Cargaremos los datos en un DataFrame usando Pandas:

1
import pandas as pd

If you're new to Pandas and DataFrames, read our "Guía de Python con Pandas: Tutorial de DataFrame con ejemplos"!

Vamos a leer el archivo CSV y empaquetarlo en un DataFrame:

1
2
3
# Substitute the path_to_file content by the path to your student_scores.csv file 
path_to_file = 'home/projects/datasets/student_scores.csv'
df = pd.read_csv(path_to_file)

Una vez que se cargan los datos, echemos un vistazo rápido a los primeros 5 valores usando el método head():

1
df.head() 

Esto resulta en:

1
2
3
4
5
6
   Hours  Scores
0    2.5      21
1    5.1      47
2    3.2      27
3    8.5      75
4    3.5      30

También podemos verificar la forma de nuestro conjunto de datos a través de la propiedad shape:

1
df.shape

Por lo general, conocer la forma de sus datos es bastante crucial para poder analizarlos y construir modelos a su alrededor:

1
(25, 2)

Tenemos 25 filas y 2 columnas, son 25 entradas que contienen un par de hora y una puntuación. Nuestra pregunta inicial era si obtendríamos una puntuación más alta si hubiéramos estudiado más tiempo. En esencia, estamos preguntando por la relación entre Horas y Puntuaciones. Entonces, ¿cuál es la relación entre estas variables? Una excelente manera de explorar las relaciones entre variables es a través de diagramas de dispersión. Trazaremos las horas en el eje X y las puntuaciones en el eje Y, y para cada par, se colocará un marcador en función de sus valores:

1
df.plot.scatter(x='Hours', y='Scores', title='Scatterplot of hours and scores percentages');

If you're new to Scatter Plots - read our "Diagrama de dispersión de Matplotlib - Tutorial y ejemplos"!

Esto resulta en:

A medida que aumentan las horas, también lo hacen las puntuaciones. ¡Aquí hay una correlación positiva bastante alta! Dado que la forma de la línea que forman los puntos parece ser recta, decimos que hay una correlación lineal positiva entre las variables Horas y Puntajes. ¿Qué tan correlacionados están? El método corr() calcula y muestra las correlaciones entre variables numéricas en un DataFrame:

1
print(df.corr())
1
2
3
           Hours    Scores
Hours   1.000000  0.976191
Scores  0.976191  1.000000

En esta tabla, Horas y Horas tienen una correlación de 1,0 (100 %), al igual que las puntuaciones tienen una correlación del 100 % con las puntuaciones, naturalmente. ¡Cualquier variable tendrá un mapeo 1:1 consigo misma! Sin embargo, la correlación entre Puntajes y Horas es 0.97. Cualquier cosa por encima de 0.8 se considera una fuerte correlación positiva.

If you'd like to read more about correlation between linear variables in detail, as well as different correlation coefficients, read our "Cálculo del coeficiente de correlación de Pearson en Python con Numpy"!

Tener una correlación lineal alta significa que, en general, podremos determinar el valor de una función en función de la otra. Incluso sin calcular, puedes decir que si alguien estudia durante 5 horas, obtendrá alrededor del 50 % como puntaje. Dado que esta relación es realmente fuerte, podremos construir un algoritmo de regresión lineal simple pero preciso para predecir el puntaje en función del tiempo de estudio, en este conjunto de datos.

Cuando tenemos una relación lineal entre dos variables, estaremos viendo una línea. Cuando hay una relación lineal entre tres, cuatro, cinco (o más) variables, estaremos ante una intersección de planos. En todos los casos, este tipo de cualidad se define en álgebra como linealidad.

Pandas también viene con un gran método de ayuda para resúmenes estadísticos, y podemos describir() el conjunto de datos para tener una idea de los valores medios, máximos, mínimos, etc. de nuestras columnas:

1
print(df.describe())
1
2
3
4
5
6
7
8
9
           Hours     Scores
count  25.000000  25.000000
mean    5.012000  51.480000
std     2.525094  25.286887
min     1.100000  17.000000
25%     2.700000  30.000000
50%     4.800000  47.000000
75%     7.400000  75.000000
max     9.200000  95.000000

Teoría de la regresión lineal

Nuestras variables expresan una relación lineal. Podemos adivinar intuitivamente el porcentaje de puntaje en función de la cantidad de horas estudiadas. Sin embargo, ¿podemos definir una forma más formal de hacer esto? Podríamos trazar una línea entre nuestros puntos y leer el valor de "Puntuación" si trazamos una línea vertical desde un valor dado de "Horas":

Gráfica de horas de estudio y puntajes de exámenes{.img-responsive}

La ecuación que describe cualquier línea recta es:
$$
y = a*x+b
$$
En esta ecuación, y representa el porcentaje de puntuación, x representa las horas estudiadas. b es donde la línea comienza en el eje Y, también llamado intersección del eje Y y a define si la línea va a estar más hacia la parte superior o inferior del gráfico (el ángulo de la línea), por lo que se llama la pendiente de la línea.

Al ajustar la pendiente y la intersección de la línea, podemos moverla en cualquier dirección. Por lo tanto, al calcular los valores de pendiente e intercepción, ¡podemos ajustar una línea para que se ajuste a nuestros datos!

¡Eso es todo! Ese es el corazón de la regresión lineal y un algoritmo realmente solo calcula los valores de la pendiente y la intersección. Utiliza los valores de x e y que ya tenemos y varía los valores de a y b. Al hacerlo, ajusta varias líneas a los puntos de datos y devuelve la línea que está más cerca de todos los puntos de datos, o la línea de mejor ajuste. Al modelar esa relación lineal, nuestro algoritmo de regresión también se denomina modelo. En este proceso, cuando intentamos determinar, o predecir el porcentaje basado en las horas, significa que nuestra variable y depende de los valores de nuestra variable x.

{.icon aria-hidden=“true”}

Nota: En Estadísticas, se acostumbra llamar a y la variable dependiente y x la variable independiente. En Ciencias de la Computación, y generalmente se denomina objetivo, etiqueta y x característica o atributo. Verás que los nombres se intercambian, ten en cuenta que suele haber una variable que queremos predecir y otra para encontrar su valor. También es una convención usar X en mayúscula en lugar de minúsculas, tanto en Estadísticas como en CS.

Regresión lineal con Scikit-learn de Python

Con la teoría bajo nuestro cinturón, ¡vamos a implementar un algoritmo de regresión lineal con Python y la biblioteca Scikit-Learn! Comenzaremos con una regresión lineal más simple y luego ampliaremos la regresión lineal múltiple con un nuevo conjunto de datos.

Preprocesamiento de datos {#preprocesamiento de datos}

En la sección anterior, ya importamos Pandas, cargamos nuestro archivo en un DataFrame y trazamos un gráfico para ver si había una indicación de una relación lineal. Ahora, podemos dividir nuestros datos en dos matrices: una para la característica dependiente y otra para la característica independiente o de destino. Dado que queremos predecir el porcentaje de puntuación en función de las horas estudiadas, nuestra y será la columna "Puntuación" y nuestra X será la columna "Horas".

Para separar el objetivo y las características, podemos atribuir los valores de la columna del marco de datos a nuestras variables y y X:

1
2
y = df['Scores'].values.reshape(-1, 1)
X = df['Hours'].values.reshape(-1, 1)

{.icon aria-hidden=“true”}

Nota: df['Column_Name'] devuelve una Serie de pandas. Algunas bibliotecas pueden funcionar en una Serie tal como lo harían en una matriz NumPy, pero no todas las bibliotecas tienen este conocimiento. En algunos casos, querrá extraer la matriz NumPy subyacente que describe sus datos. Esto se hace fácilmente a través del campo valores de la Serie.

El modelo de regresión lineal de Scikit-Learn espera una entrada 2D, y realmente estamos ofreciendo una matriz 1D si solo extraemos los valores:

1
2
print(df['Hours'].values) # [2.5 5.1 3.2 8.5 3.5 1.5 9.2 ... ]
print(df['Hours'].values.shape) # (25,)

Se espera una entrada 2D porque la clase LinearRegression() (más sobre esto más adelante) espera entradas que pueden contener más de un solo valor (pero también pueden ser un solo valor). En cualquier caso, tiene que ser una matriz 2D, donde cada elemento (hora) es en realidad una matriz de 1 elemento:

1
2
print(X.shape) # (25, 1)
print(X)      # [[2.5] [5.1]  [3.2] ... ]

Ya podríamos alimentar nuestros datos X e y directamente a nuestro modelo de regresión lineal, pero si usamos todos nuestros datos a la vez, ¿cómo podemos saber si nuestros resultados son buenos? Al igual que en el aprendizaje, lo que haremos será usar una parte de los datos para entrenar nuestro modelo y otra parte para probarlo.

Si desea leer más sobre las reglas generales, la importancia de dividir conjuntos, conjuntos de validación y el método auxiliar train_test_split(), lea nuestra guía detallada sobre "train_test_split() de Scikit-Learn - Conjuntos de entrenamiento, prueba y validación"!

Esto se logra fácilmente a través del método auxiliar train_test_split(), que acepta nuestras matrices X e y (también funciona en DataFrames y divide un solo DataFrame en conjuntos de entrenamiento y prueba), y un tamaño_prueba. El test_size es el porcentaje de los datos generales que usaremos para la prueba:

1
2
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)

El método toma muestras al azar respetando el porcentaje que hemos definido, pero respeta los pares X-y, para que el muestreo no confunda totalmente la relación. Algunas divisiones de prueba de tren comunes son 80/20 y 70/30.

Dado que el proceso de muestreo es inherentemente aleatorio, siempre obtendremos resultados diferentes al ejecutar el método. Para poder tener los mismos resultados, o resultados reproducibles, podemos definir una constante llamada SEMILLA que tiene el valor del sentido de la vida (42):

1
SEED = 42

{.icon aria-hidden=“true”}

Nota: La semilla puede ser cualquier número entero y se utiliza como semilla para la muestra aleatoria. La semilla suele ser aleatoria, lo que genera diferentes resultados. Sin embargo, si lo configura manualmente, el muestreador devolverá los mismos resultados. Es una convención usar 42 como semilla como referencia a la popular serie de novelas "La guía del autoestopista galáctico".

Luego podemos pasar esa SEED al parámetro random_state de nuestro método train_test_split:

1
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = SEED)

Ahora, si imprime su matriz X_train, encontrará las horas de estudio, y y_train contiene los porcentajes de puntuación:

1
2
print(X_train) # [[2.7] [3.3] [5.1] [3.8] ... ]
print(y_train) # [[25] [42] [47] [35] ... ]

Entrenamiento de un modelo de regresión lineal {#entrenamiento de un modelo de regresión lineal}

Ya tenemos listos nuestros sets de tren y prueba. Scikit-Learn tiene una gran cantidad de tipos de modelos que podemos importar y entrenar fácilmente, siendo LinearRegression uno de ellos:

1
2
from sklearn.linear_model import LinearRegression
regressor = LinearRegression()

Ahora, necesitamos ajustar la línea a nuestros datos, lo haremos usando el método .fit() junto con nuestros datos X_train y y_train:

1
regressor.fit(X_train, y_train)

Si no se arrojan errores, ¡el regresor encontró la mejor línea de ajuste! La línea está definida por nuestras características y la intersección/pendiente. De hecho, podemos inspeccionar la intersección y la pendiente imprimiendo los atributos regressor.intecept_ y regressor.coef_, respectivamente:

1
print(regressor.intercept_)
1
2.82689235

Para recuperar la pendiente (que también es el coeficiente de x):

1
print(regressor.coef_)

El resultado debería ser:

1
[9.68207815]

Esto se puede conectar literalmente a nuestra fórmula anterior:

$$
puntuación = 9,68207815*horas+2,82689235
$$

Verifiquemos muy rápido si esto se alinea con nuestra estimación:

$$\begin{matriz} {horas = 5} \ {puntuación = 9,68207815 \últimas horas + 2,82689235} \ {puntaje = 51.2672831} \ \end{matriz}$$

¡Con 5 horas de estudio, puede esperar un puntaje de alrededor del 51 %! Otra forma de interpretar el valor del intercepto es: si un estudiante estudia una hora más de lo que estudió previamente para un examen, puede esperar tener un aumento de 9.68% considerando el porcentaje de puntaje que había logrado previamente.

En otras palabras, el valor de la pendiente muestra lo que sucede con la variable dependiente cada vez que hay un aumento (o disminución) de una unidad de la variable independiente.

Hacer predicciones

Para evitar ejecutar cálculos nosotros mismos, podríamos escribir nuestra propia fórmula que calcula el valor:

1
2
3
4
5
def calc(slope, intercept, hours):
    return slope*hours+intercept

score = calc(regressor.coef_, regressor.intercept_, 9.5)
print(score) # [[94.80663482]]

Sin embargo, una forma mucho más práctica de predecir nuevos valores usando nuestro modelo es llamar a la función predict():

1
2
3
# Passing 9.5 in double brackets to have a 2 dimensional array
score = regressor.predict([[9.5]])
print(score) # 94.80663482

Nuestro resultado es 94.80663482, o aproximadamente 95%. Ahora tenemos una estimación del porcentaje de puntuación para todas y cada una de las horas que se nos ocurran. Pero, ¿podemos confiar en esas estimaciones? En la respuesta a esa pregunta está la razón por la que dividimos los datos en entrenar y probar en primer lugar. Ahora podemos predecir usando nuestros datos de prueba y comparar lo predicho con nuestros resultados reales: los resultados de la verdad básica.

Para hacer predicciones sobre los datos de prueba, pasamos los valores X_test al método predict(). Podemos asignar los resultados a la variable y_pred:

1
y_pred = regressor.predict(X_test)

La variable y_pred ahora contiene todos los valores predichos para los valores de entrada en X_test. Ahora podemos comparar los valores de salida reales para X_test con los valores predichos, organizándolos uno al lado del otro en una estructura de marco de datos:

1
2
df_preds = pd.DataFrame({'Actual': y_test.squeeze(), 'Predicted': y_pred.squeeze()})
print(df_preds

La salida se ve así:

1
2
3
4
5
6
   Actual  Predicted
0      81  83.188141
1      30  27.032088
2      21  27.032088
3      76  69.633232
4      62  59.951153

Aunque nuestro modelo parece no ser muy preciso, los porcentajes previstos se acercan a los reales. Cuantifiquemos la diferencia entre los valores reales y predichos para obtener una visión objetiva de cómo se está desempeñando realmente.

Evaluación del modelo

Después de mirar los datos, ver una relación lineal, entrenar y probar nuestro modelo, podemos entender qué tan bien predice usando algunas métricas. Para los modelos de regresión se utilizan principalmente tres métricas de evaluación:

  1. Mean Absolute Error (MAE): Cuando restamos los valores pronosticados de los valores reales, obteniendo los errores, sumamos los valores absolutos de esos errores y obtenemos su media. Esta métrica da una noción del error global para cada predicción del modelo, cuanto menor (más cercano a 0) mejor.

$$
mae = (\frac{1}{n})\sum_{i=1}^{n}\left | Real - Predicho \right |
$$

{.icon aria-hidden=“true”}

Nota: También puede encontrar la notación y y ŷ en las ecuaciones. La y se refiere a los valores reales y la ŷ a los valores predichos.

  1. Mean Squared Error (MSE): Es similar a la métrica MAE, pero eleva al cuadrado los valores absolutos de los errores. Además, al igual que con MAE, cuanto más pequeño o más cercano a 0, mejor. El valor de MSE se eleva al cuadrado para que los errores grandes sean aún mayores. Una cosa a la que hay que prestar mucha atención es que suele ser una métrica difícil de interpretar debido al tamaño de sus valores y al hecho de que no están en la misma escala de los datos.

$$
mse = \sum_{i=1}^{D}(Real - Predicho)^2
$$

  1. Root Mean Squared Error (RMSE): Intenta resolver el problema de interpretación planteado con el MSE obteniendo la raíz cuadrada de su valor final, para escalarlo a las mismas unidades de los datos. Es más fácil de interpretar y bueno cuando necesitamos mostrar o mostrar el valor real de los datos con el error. Muestra cuánto pueden variar los datos, por lo que, si tenemos un RMSE de 4,35, nuestro modelo puede cometer un error porque agregó 4,35 al valor real o porque necesitó 4,35 para llegar al valor real. Cuanto más cerca de 0, mejor también.

$$
rmse = \sqrt{ \sum_{i=1}^{D}(Real - Predicho)^2}
$$

Podemos usar cualquiera de esas tres métricas para comparar modelos (si necesitamos elegir uno). También podemos comparar el mismo modelo de regresión con diferentes valores de argumento o con diferentes datos y luego considerar las métricas de evaluación. Esto se conoce como ajuste de hiperparámetros: ajuste de los hiperparámetros que influyen en un algoritmo de aprendizaje y observación de los resultados.

A la hora de elegir entre modelos, los que tienen menos errores suelen rendir mejor. Al monitorear modelos, si las métricas empeoraron, entonces una versión anterior del modelo fue mejor, o hubo alguna alteración significativa en los datos para que el modelo funcionara peor de lo que estaba funcionando.

Afortunadamente, no tenemos que hacer ninguno de los cálculos de métricas manualmente. El paquete Scikit-Learn ya viene con funciones que se pueden usar para averiguar los valores de estas métricas para nosotros. Busquemos los valores de estas métricas usando nuestros datos de prueba. Primero, importaremos los módulos necesarios para calcular los errores MAE y MSE. Respectivamente, mean_absolute_error y mean_squared_error:

1
from sklearn.metrics import mean_absolute_error, mean_squared_error

Ahora, podemos calcular el MAE y el MSE pasando y_test (real) e y_pred (predicho) a los métodos. El RMSE se puede calcular tomando la raíz cuadrada del MSE, para eso, usaremos el método sqrt() de NumPy:

1
import numpy as np

Para los cálculos de métricas:

1
2
3
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)

También imprimiremos los resultados de las métricas usando la cadena f y la precisión de 2 dígitos después de la coma con :.2f:

1
2
3
print(f'Mean absolute error: {mae:.2f}')
print(f'Mean squared error: {mse:.2f}')
print(f'Root mean squared error: {rmse:.2f}')

Los resultados de las métricas se verán así:

1
2
3
Mean absolute error: 3.92
Mean squared error: 18.94
Root mean squared error: 4.35

Todos nuestros errores son bajos, y nos falta el valor real por 4,35 como máximo (más bajo o más alto), que es un rango bastante pequeño teniendo en cuenta los datos que tenemos.

Regresión lineal múltiple

Hasta este punto, hemos predicho un valor con regresión lineal usando solo una variable. Hay un escenario diferente que podemos considerar, donde podemos predecir usando muchas variables en lugar de una, y este también es un escenario mucho más común en la vida real, donde muchas cosas pueden afectar algún resultado.

Por ejemplo, si queremos predecir el consumo de gas en los estados de EE. UU., sería limitante usar solo una variable, por ejemplo, los impuestos a la gasolina, para hacerlo, ya que más que los impuestos a la gasolina afectan el consumo. Hay más cosas involucradas en el consumo de gasolina además de los impuestos a la gasolina, como el ingreso per cápita de las personas en un área determinada, la extensión de las carreteras pavimentadas, la proporción de la población que tiene una licencia de conducir, y muchos otros factores. Algunos factores afectan el consumo más que otros, ¡y aquí es donde los coeficientes de correlación realmente ayudan!

En un caso como este, cuando tiene sentido usar varias variables, la regresión lineal se convierte en una regresión lineal múltiple.

{.icon aria-hidden=“true”}

Nota: Otra nomenclatura para la regresión lineal con una variable independiente es la regresión lineal univariante. Y para la regresión lineal múltiple, con muchas variables independientes, es una regresión lineal multivariante.

Por lo general, los datos del mundo real, al tener muchas más variables con un mayor rango de valores, o más variabilidad, y también relaciones complejas entre variables, implicarán una regresión lineal múltiple en lugar de una regresión lineal simple.

Es decir, en el día a día, si hay linealidad en tus datos, probablemente estarás aplicando una regresión lineal múltiple a tus datos.

Análisis de datos exploratorios {#análisis de datos exploratorios}

Para tener una idea práctica de la regresión lineal múltiple, sigamos trabajando con nuestro ejemplo de consumo de gas y usemos un conjunto de datos que tenga datos de consumo de gas en 48 estados de EE. UU.

{.icon aria-hidden=“true”}

Nota: Puede descargar el conjunto de datos de consumo de gas en Kaggle. Puede obtener más información sobre los detalles en el conjunto de datos aquí.

Siguiendo lo que hicimos con la regresión lineal, también querremos conocer nuestros datos antes de aplicar la regresión lineal múltiple. Primero, podemos importar los datos con el método pandas read_csv():

1
2
path_to_file = 'home/projects/datasets/petrol_consumption.csv'
df = pd.read_csv(path_to_file)

Ahora podemos echar un vistazo a las primeras cinco filas con df.head():

1
df.head()

Esto resulta en:

1
2
3
4
5
6
    Petrol_tax  Average_income  Paved_Highways  Population_Driver_licence(%)  Petrol_Consumption
0   9.0         3571            1976            0.525                         541
1   9.0         4092            1250            0.572                         524
2   9.0         3865            1586            0.580                         561
3   7.5         4870            2351            0.529                         414
4   8.0         4399            431             0.544                         410

Podemos ver cuántas filas y columnas tienen nuestros datos con shape:

1
df.shape

Que muestra:

1
(48, 5)

En este conjunto de datos, tenemos 48 filas y 5 columnas. A la hora de clasificar el tamaño de un conjunto de datos, también existen diferencias entre Estadística e Informática.

En Estadística, un conjunto de datos con más de 30 o con más de 100 filas (u observaciones) ya se considera grande, mientras que en Informática, un conjunto de datos generalmente debe tener al menos 1,000-3,000 filas para ser considerado \ “grande". "Grande" también es muy subjetivo: algunos consideran 3.000 grande, mientras que otros consideran 3.000.000 grande.

No hay consenso sobre el tamaño de nuestro conjunto de datos. Sigamos explorándolo y echemos un vistazo a las estadísticas descriptivas de estos nuevos datos. Esta vez, facilitaremos la comparación de las estadísticas redondeando los valores a dos decimales con el método round() y transponiendo la tabla con la propiedad T:

1
print(df.describe().round(2).T)

Nuestra tabla ahora ocupa toda la columna en lugar de toda la fila:

1
2
3
4
5
6
                             count mean    std     min     25%     50%     75%     max
Petrol_tax                   48.0  7.67    0.95    5.00    7.00    7.50    8.12    10.00
Average_income               48.0  4241.83 573.62  3063.00 3739.00 4298.00 4578.75 5342.00
Paved_Highways               48.0  5565.42 3491.51 431.00  3110.25 4735.50 7156.00 17782.00
Population_Driver_licence(%) 48.0  0.57    0.06    0.45    0.53    0.56    0.60    0.72
Petrol_Consumption           48.0  576.77  111.89  344.00  509.50  568.50  632.75  968.00

{.icon aria-hidden=“true”}

Nota: La tabla transpuesta es mejor si queremos comparar entre estadísticas, y la tabla original es mejor si queremos comparar entre variables.

Al observar las columnas min y max de la tabla de descripción, vemos que el valor mínimo en nuestros datos es 0.45 y el valor máximo es 17,782. Esto significa que nuestro rango de datos es “17 781,55” (17 782 - 0,45 = 17 781,55), muy amplio, lo que implica que la variabilidad de nuestros datos también es alta.

Además, al comparar los valores de las columnas mean y std, como 7.67 y 0.95, 4241.83 y 573.62, etc., podemos ver que las medias están realmente lejos del estándar. desviaciones Eso implica que nuestros datos están lejos de la media, descentralizados, lo que también se suma a la variabilidad.

Ya tenemos dos indicios de que nuestros datos están dispersos, lo que no está a nuestro favor, ya que hace más difícil tener una línea que pueda caber desde 0,45 hasta 17.782, en términos estadísticos, para explicar esa variabilidad.

De cualquier manera, siempre es importante que tracemos los datos. Los datos con diferentes formas (relaciones) pueden tener las mismas estadísticas descriptivas. Entonces, sigamos adelante y veamos nuestros puntos en un gráfico.

{.icon aria-hidden=“true”}

Nota: El problema de tener datos con diferentes formas que tienen las mismas estadísticas descriptivas se define como Cuarteto de Anscombe. Puedes ver ejemplos de ello [aquí](https://www.autodesk.com/research/publications/same-stats- different-graphs).

Otro ejemplo de un coeficiente que es el mismo entre diferentes relaciones es la Correlación de Pearson (que verifica la correlación lineal):

¡Esta información claramente tiene un patrón! Sin embargo, no es lineal y los datos no tienen una correlación lineal, por lo tanto, el coeficiente de Pearson es 0 para la mayoría de ellos. También sería 0 para ruido aleatorio.

Again, if you're interested in reading more about Pearson's Coefficient, read out in-depth "Cálculo del coeficiente de correlación de Pearson en Python con Numpy"!

En nuestro escenario de regresión simple, hemos usado un diagrama de dispersión de las variables dependientes e independientes para ver si la forma de los puntos estaba cerca de una línea. En nuestro escenario actual, tenemos cuatro variables independientes y una variable dependiente. Para hacer un diagrama de dispersión con todas las variables, se requeriría una dimensión por variable, lo que daría como resultado un diagrama 5D.

Podríamos crear un gráfico 5D con todas las variables, lo que llevaría un tiempo y sería un poco difícil de leer, o podríamos trazar un gráfico de dispersión para cada una de nuestras variables independientes y variables dependientes para ver si hay una relación lineal entre ellos. .

Siguiendo la navaja de Ockham (también conocida como la navaja de Occam) y PEP20 de Python - "simple es mejor que complex" - crearemos un bucle for con un gráfico para cada variable.

{.icon aria-hidden=“true”}

Nota: La navaja de Ockham/Occam es un principio filosófico y científico que establece que se debe preferir la teoría o explicación más simple con respecto a las teorías o explicaciones complejas.

Esta vez, usaremos Seaborn, una extensión de Matplotlib que Pandas usa bajo el capó cuando traza:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import seaborn as sns # Convention alias for Seaborn

variables = ['Petrol_tax', 'Average_income', 'Paved_Highways','Population_Driver_licence(%)']

for var in variables:
    plt.figure() # Creating a rectangle (figure) for each plot
    # Regression Plot also by default includes
    # best-fitting regression line
    # which can be turned off via `fit_reg=False`
    sns.regplot(x=var, y='Petrol_Consumption', data=df).set(title=f'Regression plot of {var} and Petrol Consumption');

Observe en el código anterior que estamos importando Seaborn, creando una lista de las variables que queremos trazar y recorriendo esa lista para trazar cada variable independiente con nuestra variable dependiente.

El gráfico de Seaborn que estamos usando es regplot, que es la abreviatura de gráfico de regresión. Es un diagrama de dispersión que ya traza los datos dispersos junto con la línea de regresión. Si prefiere ver un diagrama de dispersión sin la línea de regresión, use sns.scatteplot en su lugar.

Estas son nuestras cuatro parcelas:

Al observar los gráficos de registro, parece que Petrol_tax y Average_income tienen una relación lineal negativa débil con Petrol_Conquisition. También parece que Population_Driver_license(%) tiene una fuerte relación lineal positiva con Petrol_Conquisition, y que la variable Paved_Highways no tiene relación con Petrol_Consumption.

También podemos calcular la correlación de las nuevas variables, esta vez usando heatmap() de Seaborn para ayudarnos a detectar las correlaciones más fuertes y más débiles basadas en tonos más cálidos (rojos) y más fríos (azules):

1
2
3
correlations = df.corr()
# annot=True displays the correlation values
sns.heatmap(correlations, annot=True).set(title='Heatmap of Consumption Data - Pearson Correlations');

¡Parece que el mapa de calor corrobora nuestro análisis anterior! ‘Impuesto_gasolina’ e ‘Ingreso_promedio’ tienen una relación lineal negativa débil de, respectivamente, ‘-0,45’ y ‘-0,24’ con ‘Consumo_gasolina’. Population_Driver_license(%) tiene una fuerte relación lineal positiva de 0.7 con Petrol_Consumption, y la correlación de Paved_Highways es de 0.019, lo que indica que no hay relación con Petrol_Consumption.

La correlación no implica causalidad, pero podemos encontrar causalidad si podemos explicar con éxito los fenómenos con nuestro modelo de regresión.

Otra cosa importante a tener en cuenta en los regplots es que hay algunos puntos muy alejados de donde se concentran la mayoría de los puntos, ya esperábamos algo así después de la gran diferencia entre las columnas media y estándar: esos puntos podrían ser datos *atípicos * y valores extremos.

{.icon aria-hidden=“true”}

Nota: Los valores atípicos y extremos tienen definiciones diferentes. Si bien los valores atípicos no siguen la dirección natural de los datos y se alejan de la forma que tienen, los valores extremos están en la misma dirección que otros puntos, pero son demasiado altos o demasiado bajos en esa dirección, lejos de los extremos. en el gráfico.

Un modelo de regresión lineal, ya sea uni o multivariable, tendrá en cuenta estos valores atípicos y extremos al determinar la pendiente y los coeficientes de la línea de regresión. Teniendo en cuenta lo que ya saben de la fórmula de regresión lineal:

$$
puntuación = 9,68207815*horas+2,82689235
$$

Si tenemos un punto atípico de 200 horas, eso podría haber sido un error de escritura; aún se usará para calcular el puntaje final:

$$\begin{matriz} {puntuación = 9,68207815 \ast 200 + 2,82689235} \ {puntuación = 1939.24252235} \ \end{matriz}$$

Solo un valor atípico puede hacer que nuestro valor de pendiente sea 200 veces mayor. Lo mismo vale para la regresión lineal múltiple. La fórmula de regresión lineal múltiple es básicamente una extensión de la fórmula de regresión lineal con más valores de pendiente:

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

La principal diferencia entre esta fórmula y la anterior es que se describe como plano, en lugar de describir una línea. Sabemos que tenemos b~n~ * x~n~ coeficientes en lugar de solo * x.

{.icon aria-hidden=“true”}

Nota: Se agregó un error al final de la fórmula de regresión lineal múltiple, que es un error entre los valores predichos y reales, o error residual. Este error suele ser tan pequeño que se omite en la mayoría de las fórmulas:

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

De la misma forma, si tenemos un valor extremo de 17.000, acabará por hacer nuestra pendiente 17.000 más grande:

$$
y = b_0 + 17,000 * x_1 + b_2 * x_2 + b_3 * x_3 + \ldots + b_n * x_n
$$

En otras palabras, los modelos lineales univariados y multivariados son sensibles a valores atípicos y valores de datos extremos.

{.icon aria-hidden=“true”}

Nota: Está fuera del alcance de esta guía, pero puede ir más allá en el análisis de datos y la preparación de datos para el modelo observando los diagramas de caja, tratando los valores atípicos y los valores extremos.

If you'd like to learn more about Violin Plots and Box Plots - read our diagrama de caja and Trama de violín guides!

Hemos aprendido mucho sobre modelos lineales y análisis de datos exploratorios, ahora es el momento de usar Ingreso_promedio, Carreteras_pavimentadas, Población_licencia de conducir (%) y Impuesto_gasolina como variables independientes de nuestro modelo y ver qué sucede .

Preparación de los datos

Siguiendo lo que se ha hecho con la regresión lineal simple, después de cargar y explorar los datos, podemos dividirlos en características y objetivos. La principal diferencia es que ahora nuestras características tienen 4 columnas en lugar de una.

Podemos usar corchetes dobles [[ ]] para seleccionarlos del marco de datos:

1
2
3
y = df['Petrol_Consumption']
X = df[['Average_income', 'Paved_Highways',
       'Population_Driver_licence(%)', 'Petrol_tax']]

Después de configurar nuestros conjuntos X e y, podemos dividir nuestros datos en conjuntos de entrenamiento y prueba. Usaremos la misma semilla y el 20% de nuestros datos para el entrenamiento:

1
2
3
X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size=0.2, 
                                                    random_state=SEED)

Entrenamiento del modelo multivariante

Después de dividir los datos, podemos entrenar nuestro modelo de regresión múltiple. Tenga en cuenta que ahora no hay necesidad de remodelar nuestros datos X, una vez que ya tiene más de una dimensión:

1
X.shape # (48, 4)

Para entrenar nuestro modelo podemos ejecutar el mismo código que antes, y usar el método fit() de la clase LinearRegression:

1
2
regressor = LinearRegression()
regressor.fit(X_train, y_train)

Después de ajustar el modelo y encontrar nuestra solución óptima, también podemos mirar la intersección:

1
regressor.intercept_
1
361.45087906668397

Y en los coeficientes de las características

1
regressor.coef_
1
[-5.65355145e-02, -4.38217137e-03,  1.34686930e+03, -3.69937459e+01]

Esos cuatro valores son los coeficientes para cada una de nuestras características en el mismo orden en que los tenemos en nuestros datos X. Para ver una lista con sus nombres, podemos usar el atributo columns del dataframe:

1
feature_names = X.columns

Ese código generará:

1
['Average_income', 'Paved_Highways', 'Population_Driver_licence(%)', 'Petrol_tax']

Teniendo en cuenta que es un poco difícil ver las características y los coeficientes juntos de esta manera, podemos organizarlos mejor en un formato de tabla.

Para hacer eso, podemos asignar nuestros nombres de columna a una variable feature_names y nuestros coeficientes a una variable model_coficients. Después de eso, podemos crear un marco de datos con nuestras características como índice y nuestros coeficientes como valores de columna llamados coficients_df:

1
2
3
4
5
6
7
feature_names = X.columns
model_coefficients = regressor.coef_

coefficients_df = pd.DataFrame(data = model_coefficients, 
                              index = feature_names, 
                              columns = ['Coefficient value'])
print(coefficients_df)

El DataFrame final debería verse así:

1
2
3
4
5
                              Coefficient value
Average_income                        -0.056536
Paved_Highways                        -0.004382
Population_Driver_licence(%)        1346.869298
Petrol_tax                           -36.993746

Si en el modelo de regresión lineal teníamos 1 variable y 1 coeficiente, ahora en el modelo de regresión lineal múltiple tenemos 4 variables y 4 coeficientes. ¿Qué pueden significar esos coeficientes? Siguiendo la misma interpretación de los coeficientes de la regresión lineal, esto significa que por una unidad de aumento en el ingreso promedio, hay una disminución de 0.06 dólares en el consumo de gas.

De manera similar, por una unidad de aumento en carreteras pavimentadas, hay una disminución de 0.004 en millas de consumo de gasolina; y por una unidad de incremento en la proporción de población con licencia de conducir, hay un incremento de 1,346 billones de galones de consumo de gasolina.

Y, por último, por un aumento unitario en el impuesto a las gasolinas, se produce una disminución de 36.993 millones de galones en el consumo de gas.

Al observar el marco de datos de los coeficientes, también podemos ver que, de acuerdo con nuestro modelo, las funciones “Ingreso_promedio” y “Carreteras_pavimentadas” son las que están más cerca de 0, lo que significa que tienen el menor impacto en el consumo de gas. Mientras que Population_Driver_license(%) y Petrol_tax, con los coeficientes de 1346,86 y -36,99, respectivamente, tienen el mayor impacto en nuestra predicción objetivo.

En otras palabras, el consumo de gasolina se explica principalmente por el porcentaje de la población con licencia de conducir y el monto del impuesto a la gasolina, sorprendentemente (o como era de esperar) bastante.

Podemos ver cómo este resultado tiene una conexión con lo que habíamos visto en el mapa de calor de correlación. El porcentaje de la licencia de conducir tuvo la correlación más fuerte, por lo que se esperaba que pudiera ayudar a explicar el consumo de gasolina, y el impuesto a la gasolina tuvo una correlación negativa débil, pero, en comparación con el ingreso promedio, también tuvo una correlación negativa débil. - fue la correlación negativa la que más se acercó a -1 y acabó explicando el modelo.

Cuando se sumaron todos los valores a la fórmula de regresión múltiple, las carreteras pavimentadas y las pendientes de ingreso promedio terminaron acercándose a 0, mientras que el porcentaje de licencia de conducir y el ingreso fiscal se alejaron más de 0. Por lo que esas variables se tomaron más en cuenta al encontrar la mejor línea ajustada.

{.icon aria-hidden=“true”}

Nota: En la ciencia de datos, tratamos principalmente con hipótesis e incertidumbres. No hay 100% de certeza y siempre hay un error. Si tiene 0 errores o puntajes del 100%, sospeche. Hemos entrenado solo un modelo con una muestra de datos, es demasiado pronto para asumir que tenemos un resultado final. Para ir más allá, puede realizar análisis residuales, entrenar el modelo con diferentes muestras utilizando una técnica de validación cruzada. También podría obtener más datos y más variables para explorar y conectar el modelo para comparar resultados.

Parece que nuestro análisis tiene sentido hasta ahora. Ahora es el momento de determinar si nuestro modelo actual es propenso a errores.

Realización de predicciones con el modelo de regresión multivariante {#realización de predicciones con el modelo de regresión multivariante}

Para comprender si nuestro modelo comete errores y cómo, podemos predecir el consumo de gas utilizando nuestros datos de prueba y luego mirar nuestras métricas para poder saber qué tan bien se está comportando nuestro modelo.

De la misma manera que lo habíamos hecho para el modelo de regresión simple, pronostiquemos con los datos de prueba:

1
y_pred = regressor.predict(X_test)

Ahora que tenemos nuestras predicciones de prueba, podemos compararlas mejor con los valores de salida reales para X_test organizándolos en un formato DataFrame:

1
2
results = pd.DataFrame({'Actual': y_test, 'Predicted': y_pred})
print(results)

La salida debería verse así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    Actual   Predicted
27     631  606.692665
40     587  673.779442
26     577  584.991490
43     591  563.536910
24     460  519.058672
37     704  643.461003
12     525  572.897614
19     640  687.077036
4      410  547.609366
25     566  530.037630

Aquí tenemos el índice de la fila de cada dato de prueba, una columna para su valor real y otra para sus valores predichos. Cuando observamos la diferencia entre los valores real y pronosticado, como entre 631 y 607, que es 24, o entre 587 y 674, es decir -87, parece que hay cierta distancia entre ambos valores, pero ¿es esa distancia demasiado? ?

Evaluación del modelo multivariado

Después de explorar, entrenar y observar las predicciones de nuestro modelo, nuestro paso final es evaluar el rendimiento de nuestra regresión lineal múltiple. Queremos entender si nuestros valores predichos están demasiado lejos de nuestros valores reales. Haremos esto de la misma manera que lo hicimos anteriormente, calculando las métricas MAE, MSE y RMSE.

Entonces, vamos a ejecutar el siguiente código:

1
2
3
4
5
6
7
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)

print(f'Mean absolute error: {mae:.2f}')
print(f'Mean squared error: {mse:.2f}')
print(f'Root mean squared error: {rmse:.2f}')

El resultado de nuestras métricas debería ser:

1
2
3
Mean absolute error: 53.47
Mean squared error: 4083.26
Root mean squared error: 63.90

Podemos ver que el valor del RMSE es 63,90, lo que significa que nuestro modelo podría equivocarse en su predicción al sumar o restar 63,90 del valor real. Sería mejor tener este error más cerca de 0, y 63,90 es un número grande; esto indica que nuestro modelo podría no estar prediciendo muy bien.

Nuestro MAE también está lejos de 0. Podemos ver una diferencia significativa en la magnitud cuando lo comparamos con nuestra regresión simple anterior donde obtuvimos un mejor resultado.

Para profundizar más en lo que le está sucediendo a nuestro modelo, podemos ver una métrica que mide el modelo de una manera diferente, no considera nuestros valores de datos individuales como MSE, RMSE y MAE, sino que adopta un enfoque más general. al error, el R^2^:

$$
R^2 = 1 - \frac{\sum(Real - Predicho)^2}{\sum(Real - Real \ Media)^2}
$$

El R^2^ no nos dice qué tan lejos o cerca está cada valor predicho de los datos reales; nos dice cuánto de nuestro objetivo está siendo capturado por nuestro modelo.

En otras palabras, R^2^ cuantifica qué parte de la varianza de la variable dependiente está siendo explicada por el modelo.

La métrica R^2^ varía de 0% a 100%. Cuanto más cerca del 100%, mejor. Si el valor de R^2^ es negativo, significa que no explica el objetivo en absoluto.

Podemos calcular R^2^ en Python para comprender mejor cómo funciona:

1
2
3
4
actual_minus_predicted = sum((y_test - y_pred)**2)
actual_minus_actual_mean = sum((y_test - y_test.mean())**2)
r2 = 1 - actual_minus_predicted/actual_minus_actual_mean
print('R²:', r2)
1
: 0.39136640014305457

R^2^ también viene implementado por defecto en el método score de la clase de regresor lineal de Scikit-Learn. Podemos calcularlo así:

1
regressor.score(X_test, y_test)

Esto resulta en:

1
0.39136640014305457

Hasta ahora, parece que nuestro modelo actual explica solo el 39 % de nuestros datos de prueba, lo que no es un buen resultado, significa que deja el 61 % de los datos de prueba sin explicar.

Entendamos también cuánto explica nuestro modelo de los datos de nuestro tren:

1
regressor.score(X_train, y_train)

Qué salidas:

1
0.7068781342155135

Hemos encontrado un problema con nuestro modelo. Explica el 70 % de los datos del tren, pero solo el 39 % de nuestros datos de prueba, lo cual es más importante de acertar que los datos de nuestro tren. Está ajustando muy bien los datos del tren y no puede ajustar los datos de prueba, lo que significa que tenemos un modelo de regresión lineal múltiple sobreajustado.

Hay muchos factores que pueden haber contribuido a esto, algunos de ellos podrían ser:

  1. Necesidad de más datos: solo tenemos un año de datos (y solo 48 filas), que no es mucho, mientras que tener varios años de datos podría haber ayudado a mejorar bastante los resultados de predicción.
  2. Superar el sobreajuste: podemos usar una validación cruzada que ajustará nuestro modelo a diferentes muestras mezcladas de nuestro conjunto de datos para intentar terminar con el sobreajuste.
  3. Suposiciones que no se cumplen: hemos hecho la suposición de que los datos tenían una relación lineal, pero ese podría no ser el caso. Visualizar los datos usando diagramas de caja, comprender la distribución de datos, tratar los valores atípicos y normalizarlos puede ayudar con eso.
  4. Características deficientes: es posible que necesitemos otras o más características que tengan una relación más fuerte con los valores que estamos tratando de predecir.

Yendo más lejos: proyecto de extremo a extremo portátil

¿Tu naturaleza inquisitiva te hace querer ir más allá? Recomendamos consultar nuestro Proyecto guiado: ["Predicción práctica del precio de la vivienda: aprendizaje automático en Python"](https://wikihtp.com/courses/hands-on-house- precio-predicción-aprendizaje-máquina-en-python/#cta){target="_blank”}.

[](https://wikihtp.com/ cursos/predicción-de-precio-de-la-casa-práctica-aprendizaje-de-máquina-en-python/#cta)

En este proyecto guiado, aprenderá a crear potentes modelos tradicionales de aprendizaje automático, así como modelos de aprendizaje profundo, utilizar Ensemble Learning y capacitar a los meta-aprendices para predecir los precios de la vivienda a partir de una bolsa de modelos Scikit-Learn y Keras.

Con Keras, la API de aprendizaje profundo creada sobre Tensorflow, experimentaremos con arquitecturas, crearemos un conjunto de modelos apilados y entrenaremos una red neuronal meta-aprendizaje (modelo de nivel 1) para averiguar el precio de un casa.

El aprendizaje profundo es sorprendente, pero antes de recurrir a él, se recomienda intentar resolver el problema con técnicas más simples, como los algoritmos de aprendizaje superficial. Nuestro rendimiento de referencia se basará en un algoritmo de Regresión de bosque aleatorio. Además, exploraremos la creación de conjuntos de modelos a través de Scikit-Learn a través de técnicas como embalaje y votación.

Este es un proyecto integral y, como todos los proyectos de aprendizaje automático, comenzaremos con Análisis exploratorio de datos, seguido de Preprocesamiento de datos y, finalmente, Creación de modelos de aprendizaje superficial y Profundo para ajustarse a los datos que hemos explorado y limpiado previamente.

Conclusión

En este artículo hemos estudiado uno de los algoritmos de aprendizaje automático más fundamentales, es decir, la regresión lineal. Implementamos tanto la regresión lineal simple como la regresión lineal múltiple con la ayuda de la biblioteca de aprendizaje automático Scikit-learn. n.