Datos de escalado de funciones con Scikit-Learn para aprendizaje automático en Python

En esta guía, veremos cómo y por qué realizar el escalado de funciones para proyectos de aprendizaje automático mediante la biblioteca ScikitLearn de Python.

Introducción

El preprocesamiento de datos es un paso clave que a menudo se pasa por alto en el aprendizaje automático. De hecho, es tan importante como el modelo brillante que quieres que encaje con él.

Basura adentro - basura afuera.

Puedes tener el mejor modelo diseñado para cualquier tipo de problema: si lo alimentas con basura, arrojará basura. Vale la pena señalar que "basura" no se refiere a datos aleatorios. Es una etiqueta dura que asignamos a cualquier dato que no permita que el modelo funcione lo mejor posible, algunos más que otros. Dicho esto, los mismos datos pueden ser malos para un modelo, pero excelentes para otro. En general, varios modelos de Machine Learning no generalizan tan bien los datos con una variación de escala alta, por lo que normalmente querrá solucionarlo antes de introducirlo en un modelo.

Normalización y Estandarización son dos técnicas comúnmente utilizadas durante el Preprocesamiento de datos para ajustar las características a una escala común.

En esta guía, profundizaremos en lo que es Feature Scaling y escalaremos las características de un conjunto de datos a una escala más adecuada. Luego, entrenaremos un modelo SGDRegressor en los datos originales y escalados para verificar si tuvo mucho efecto en este conjunto de datos específico.

¿Qué es el escalado de características? Normalización y estandarización {#qué es el escalado de características, normalización y estandarización}

Escalado o Escalado de características es el proceso de cambiar la escala de ciertas características a una común. Esto generalmente se logra a través de la normalización y la estandarización (técnicas de escalado).

  • Normalización es el proceso de escalar datos en un rango de [0, 1]. Es más útil y común para tareas de regresión.

$$
x' = \frac{x-x_{min}}{x_{max} - x_{min}}
$$

  • Estandarización es el proceso de escalar los datos para que tengan un valor medio de 0 y una desviación estándar de 1. Es más útil y común para tareas de clasificación.

$$
x' = \frac{x-\mu}{\sigma}
$$

Una distribución normal con estos valores se denomina distribución normal estándar.

Vale la pena señalar que la estandarización de los datos no garantiza que estarán dentro del rango [0, 1]. Lo más probable es que no lo sea, lo que puede ser un problema para ciertos algoritmos que esperan este rango.

Para realizar la estandarización, Scikit-Learn nos proporciona la clase StandardScaler.

La normalización también se conoce como Min-Max Scaling y Scikit-Learn proporciona el MinMaxScaler para este fin. Por otro lado, también proporciona un Normalizador, que puede hacer las cosas un poco confusas.

Nota: La clase Normalizer no realiza el mismo escalado que MinMaxScaler. Normalizer funciona en filas, no en funciones, y las escala de forma independiente.

¿Cuándo realizar el escalado de funciones?

Feature Scaling no garantiza un mejor rendimiento del modelo para todos los modelos.

Por ejemplo, Feature Scaling no hace mucho si la escala no importa. Para la Agrupación de K-Means, la distancia euclidiana es importante, por lo que Feature Scaling tiene un gran impacto. También tiene un gran impacto para cualquier algoritmo que se base en gradientes, como los modelos lineales que se ajustan minimizando la pérdida con Descenso de gradiente.

Análisis de componentes principales (PCA) also suffers from data that isn't scaled properly.

En el caso de Scikit-Learn, no verá ninguna diferencia tangible con una Regresión Lineal, pero verá una diferencia sustancial con un SGDRegressor, porque un SGDRegressor, que también es un modelo lineal, depende de Descenso de gradiente estocástico para ajustarse a los parámetros.

A modelo basado en árbol won't suffer from unscaled data, because scale doesn't affect them at all, but if you perform Aumento de gradiente en clasificadores, the scale does affect learning.

Importación de datos y análisis exploratorio de datos

Estaremos trabajando con el Conjunto de datos de vivienda de Ames que contiene 79 características sobre casas vendidas en Ames, Iowa, así como como su precio de venta. Este es un gran conjunto de datos para el entrenamiento de regresión básico y avanzado, ya que hay muchas características para modificar y jugar, que en última instancia, generalmente afectan el precio de venta de una forma u otra.

{.icon aria-hidden=“true”}

Consejo: Si desea profundizar en un proyecto de regresión de extremo a extremo, consulte nuestro Proyecto guiado: [Predicción práctica del precio de la vivienda con aprendizaje automático en Python](https://wikihtp .com/courses/hands-on-house-price-prediction-machine-learning-in-python){target="_blank"}.

Importemos los datos y echemos un vistazo a algunas de las características que usaremos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import pandas as pd
import matplotlib.pyplot as plt

# Load the Dataset
df = pd.read_csv('AmesHousing.csv')
# Single out a couple of predictor variables and labels ('SalePrice' is our target label set)
x = df[['Gr Liv Area', 'Overall Qual']].values
y = df['SalePrice'].values

fig, ax = plt.subplots(ncols=2, figsize=(12, 4))

ax[0].scatter(x[:,0], y)
ax[1].scatter(x[:,1], y)

plt.show()

Existe una fuerte correlación positiva clara entre la función "Gr Liv Area" y la función "SalePrice", con solo un par de valores atípicos. También existe una fuerte correlación positiva entre la función "Calidad general" y el "Precio de oferta":

Aunque estos están en una escala muy diferente: el "Gr Liv Area" abarca hasta ~5000 (medido en pies cuadrados), mientras que la característica "Calidad general" abarca hasta 10 (categorías discretas de calidad). Si tuviéramos que trazar estos dos en los mismos ejes, no podríamos decir mucho sobre la función "Calidad general":

1
2
3
4
fig, ax = plt.subplots(figsize=(12, 4))

ax.scatter(x[:,0], y)
ax.scatter(x[:,1], y)

Además, si tuviéramos que graficar sus distribuciones, tampoco tendríamos mucha suerte:

1
2
3
4
fig, ax = plt.subplots(figsize=(12, 4))

ax.hist(x[:,0])
ax.hist(x[:,1])

La escala de estas características es tan diferente que realmente no podemos distinguirlas si las trazamos juntas. Aquí es donde entra en juego el escalado de funciones.

Escalador estándar

La clase StandardScaler se usa para transformar los datos estandarizándolos. Vamos a importarlo y escalar los datos a través de su método fit_transform():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import pandas as pd
import matplotlib.pyplot as plt
# Import StandardScaler
from sklearn.preprocessing import StandardScaler

fig, ax = plt.subplots(figsize=(12, 4))

scaler = StandardScaler()
x_std = scaler.fit_transform(x)

ax.hist(x_std[:,0])
ax.hist(x_std[:,1])

Nota: Estamos usando fit_transform() en la totalidad del conjunto de datos aquí para demostrar el uso de la clase StandardScaler y visualizar sus efectos. Al construir un modelo o canalización, como lo haremos en breve, no debe “ajustar_transformar ()” la totalidad del conjunto de datos, sino simplemente “ajustar ()” los datos de entrenamiento y “transformar ()” los datos de prueba .

Ejecutar este fragmento de código calculará los parámetros μ y σ; este proceso se conoce como ajustar los datos y luego transformarlos para que estos valores correspondan a 1 y *0 * respectivamente.

Cuando graficamos las distribuciones de estas características ahora, seremos recibidos con una trama mucho más manejable:

Si tuviéramos que trazar estos a través de diagramas de dispersión una vez más, quizás veríamos más claramente los efectos de la estandarización:

1
2
3
4
5
6
7
fig, ax = plt.subplots(figsize=(12, 4))

scaler = StandardScaler()
x_std = scaler.fit_transform(x)

ax.scatter(x_std[:,0], y)
ax.scatter(x_std[:,1], y)

MinMaxScaler

Para normalizar características, usamos la clase MinMaxScaler. Funciona de la misma manera que StandardScaler, pero utiliza un enfoque fundamentalmente diferente para escalar los datos:

1
2
3
4
5
6
7
fig, ax = plt.subplots(figsize=(12, 4))

scaler = MinMaxScaler()
x_minmax = scaler.fit_transform(x)

ax.hist(x_minmax [:,0])
ax.hist(x_minmax [:,1])

Están normalizados en el rango de [0, 1]. Si tuviéramos que trazar las distribuciones de nuevo, seríamos recibidos con:

La * asimetría * de la distribución se conserva, a diferencia de la * estandarización * que hace que se superpongan mucho más. Sin embargo, si tuviéramos que graficar los datos a través de diagramas de dispersión nuevamente:

1
2
3
4
5
6
7
fig, ax = plt.subplots(figsize=(12, 4))

scaler = MinMaxScaler()
x_minmax = scaler.fit_transform(x)

ax.scatter(x_minmax [:,0], y)
ax.scatter(x_minmax [:,1], y)

Podríamos ver la fuerte correlación positiva entre ambos con el "SalePrice" con la característica, pero la característica "Overall Qual" se extiende torpemente hacia la derecha, porque los valores atípicos de la característica "Gr Liv Area" obligó a la mayoría de su distribución a seguir en el lado izquierdo.

Efectos de valores atípicos

Tanto la normalización como la estandarización son sensibles a los valores atípicos: basta con que el conjunto de datos tenga un único valor atípico que esté muy por ahí para que las cosas se vean realmente raras. Agreguemos una entrada sintética a la función "Gr Liv Area" para ver cómo afecta el proceso de escalado:

1
2
3
4
5
6
fig, ax = plt.subplots(figsize=(12, 4))

scaler = MinMaxScaler()
x_minmax = scaler.fit_transform(x)

ax.scatter(x_minmax [:,0], y)

El único valor atípico, en el extremo derecho de la gráfica, realmente ha afectado la nueva distribución. Todos los datos, excepto el valor atípico, se ubican en los dos primeros cuartiles:

1
2
3
4
5
6
fig, ax = plt.subplots(figsize=(12, 4))

scaler = MinMaxScaler()
x_minmax = scaler.fit_transform(x)

ax.hist(x_minmax [:,0])

Escalado de características a través de Scikit-Learn Pipelines

Finalmente, sigamos adelante y entrenemos un modelo con y sin características de escalado de antemano. Cuando trabajamos en proyectos de aprendizaje automático, normalmente tenemos una tubería para los datos antes de que lleguen al modelo que estamos ajustando.

Usaremos la clase Pipeline que nos permite minimizar y, hasta cierto punto, automatizar este proceso, aunque solo tenemos dos pasos: escalar los datos y ajustar un modelo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.linear_model import SGDRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_absolute_error
import sklearn.metrics as metrics

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Import Data
df = pd.read_csv('AmesHousing.csv')
x = df[['Gr Liv Area', 'Overall Qual']].values
y = df['SalePrice'].values

# Split into a training and testing set
X_train, X_test, Y_train, Y_test = train_test_split(x, y)

# Define the pipeline for scaling and model fitting
pipeline = Pipeline([
    ("MinMax Scaling", MinMaxScaler()),
    ("SGD Regression", SGDRegressor())
])

# Scale the data and fit the model
pipeline.fit(X_train, Y_train)

# Evaluate the model
Y_pred = pipeline.predict(X_test)
print('Mean Absolute Error: ', mean_absolute_error(Y_pred, Y_test))
print('Score', pipeline.score(X_test, Y_test))

Esto resulta en:

1
2
Mean Absolute Error:  27614.031131858766
Score 0.7536086980531018

El error absoluto medio es ~27000, y la puntuación de precisión es ~75%. Esto significa que, en promedio, nuestro modelo pierde el precio por $27000, lo que no suena tan mal, aunque podría mejorarse más allá de esto.

En particular, el tipo de modelo que usamos es un poco demasiado rígido y no hemos incluido muchas características, por lo que estos dos son definitivamente los lugares que se pueden mejorar.

Sin embargo, no perdamos el foco de lo que nos interesa. ¿Cómo funciona este modelo sin Feature Scaling? Modifiquemos la canalización para omitir el paso de escalado:

1
2
3
pipeline = Pipeline([
    ("SGD Regression", SGDRegressor())
])

Lo que sucede podría sorprenderte:

1
2
Mean Absolute Error:  1260383513716205.8
Score -2.772781517117743e+20

Hemos pasado de una precisión del ~75 % a una precisión del ~-3 % simplemente saltando para escalar nuestras características. Cualquier algoritmo de aprendizaje que dependa de la escala de las características normalmente obtendrá grandes beneficios de Feature Scaling. Los que no, no verán mucha diferencia.

Por ejemplo, si entrenamos una ‘Regresión lineal’ con estos mismos datos, con y sin escalado, veremos resultados normales en nombre del escalado y resultados decentes en nombre del modelo en sí:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
pipeline1 = Pipeline([
    ("Linear Regression", LinearRegression())
])

pipeline2 = Pipeline([
    ("Scaling", StandardScaler()),
    ("Linear Regression", LinearRegression())
])

pipeline1.fit(X_train, Y_train)
pipeline2.fit(X_train, Y_train)

Y_pred1 = pipeline1.predict(X_test)
Y_pred2 = pipeline2.predict(X_test)

print('Pipeline 1 Mean Absolute Error: ', mean_absolute_error(Y_pred1, Y_test))
print('Pipeline 1 Score', pipeline1.score(X_test, Y_test))

print('Pipeline 2 Mean Absolute Error: ', mean_absolute_error(Y_pred2, Y_test))
print('Pipeline 2 Score', pipeline2.score(X_test, Y_test))
1
2
3
4
5
Pipeline 1 Mean Absolute Error:  27706.61376199076
Pipeline 1 Score 0.7641840816646945

Pipeline 2 Mean Absolute Error:  27706.613761990764
Pipeline 2 Score 0.7641840816646945

Conclusión

Feature Scaling es el proceso de escalar los valores de las características a una escala más manejable. Por lo general, lo realizará antes de introducir estas funciones en los algoritmos que se ven afectados por la escala, durante la fase de preprocesamiento.

En esta guía, hemos analizado qué es Feature Scaling y cómo realizarlo en Python con Scikit-Learn, utilizando StandardScaler para realizar la estandarización y MinMaxScaler para realizar la normalización. También hemos analizado cómo los valores atípicos afectan estos procesos y la diferencia entre un modelo sensible a la escala que se entrena con y sin escalado de características.