En Machine Learning y Data Science, a menudo nos encontramos con un término llamado Distribución de datos desequilibrada , que generalmente ocurre cuando las observaciones en una de las clases son mucho más altas o más bajas que las otras clases. Como los algoritmos de Machine Learning tienden a aumentar la precisión al reducir el error, no consideran la distribución de clases. Este problema prevalece en ejemplos como Detección de fraude, Detección de anomalías , Reconocimiento facial , etc.
Las técnicas estándar de ML, como el árbol de decisión y la regresión logística, tienen un sesgo hacia la clase mayoritaria y tienden a ignorar la clase minoritaria. Tienden solo a predecir la clase mayoritaria, por lo tanto, tienen una clasificación errónea importante de la clase minoritaria en comparación con la clase mayoritaria. En palabras más técnicas, si tenemos una distribución de datos desequilibrada en nuestro conjunto de datos, nuestro modelo se vuelve más propenso al caso en que la clase minoritaria tiene un recuerdo insignificante o muy bajo .
Técnicas de manejo de datos desequilibrados: existen principalmente 2 algoritmos que se utilizan ampliamente para manejar la distribución de clases desequilibrada.
- SMOTE
- Algoritmo de error cercano
SMOTE (técnica de sobremuestreo de minorías sintéticas) – Sobremuestreo
SMOTE (técnica de sobremuestreo de minorías sintéticas) es uno de los métodos de sobremuestreo más utilizados para resolver el problema del desequilibrio.
Su objetivo es equilibrar la distribución de clases aumentando aleatoriamente los ejemplos de clases minoritarias al replicarlos.
SMOTE sintetiza nuevas instancias minoritarias entre instancias minoritarias existentes. Genera los registros de entrenamiento virtual por interpolación lineal para la clase minoritaria. Estos registros de entrenamiento sintéticos se generan seleccionando aleatoriamente uno o más de los k-vecinos más cercanos para cada ejemplo en la clase minoritaria. Después del proceso de sobremuestreo, los datos se reconstruyen y se pueden aplicar varios modelos de clasificación para los datos procesados.
¡Más información profunda sobre cómo funciona el algoritmo SMOTE!
en el que rand(0, 1) representa el número aleatorio entre 0 y 1.
Algoritmo NearMiss – Submuestreo
NearMiss es una técnica de submuestreo. Su objetivo es equilibrar la distribución de clases eliminando aleatoriamente los ejemplos de clases mayoritarias. Cuando las instancias de dos clases diferentes están muy cerca una de la otra, eliminamos las instancias de la clase mayoritaria para aumentar los espacios entre las dos clases. Esto ayuda en el proceso de clasificación.
Para evitar el problema de la pérdida de información en la mayoría de las técnicas de submuestreo, se utilizan ampliamente los métodos de vecinos cercanos .
La intuición básica sobre el funcionamiento de los métodos del vecino cercano es la siguiente:
Para encontrar n instancias más cercanas en la clase mayoritaria, existen varias variaciones de la aplicación del algoritmo NearMiss:
- NearMiss – Versión 1: Selecciona muestras de la clase mayoritaria para las cuales las distancias promedio a las k instancias más cercanas de la clase minoritaria es menor.
- NearMiss – Versión 2: Selecciona muestras de la clase mayoritaria para las cuales la distancia promedio a las k instancias más lejanas de la clase minoritaria es menor.
- NearMiss – Versión 3: Funciona en 2 pasos. En primer lugar, para cada instancia de clase minoritaria, se almacenarán sus M vecinos más cercanos . Luego, finalmente, se seleccionan las instancias de clase mayoritaria para las cuales la distancia promedio a los N vecinos más cercanos es la mayor.
Este artículo ayuda a una mejor comprensión y práctica sobre cómo elegir mejor entre diferentes técnicas de manejo de datos desequilibrados.
Cargar bibliotecas y archivo de datos
El conjunto de datos consiste en transacciones realizadas con tarjetas de crédito. Este conjunto de datos tiene 492 transacciones fraudulentas de 284 807 transacciones . Eso lo hace muy desequilibrado, la clase positiva (fraudes) representa el 0,172% de todas las transacciones.
El conjunto de datos se puede descargar desde aquí .
# import necessary modules import pandas as pd import matplotlib.pyplot as plt import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.metrics import confusion_matrix, classification_report # load the data set data = pd.read_csv('creditcard.csv') # print info about columns in the dataframe print(data.info())
Producción:
RangeIndex: 284807 entries, 0 to 284806 Data columns (total 31 columns): Time 284807 non-null float64 V1 284807 non-null float64 V2 284807 non-null float64 V3 284807 non-null float64 V4 284807 non-null float64 V5 284807 non-null float64 V6 284807 non-null float64 V7 284807 non-null float64 V8 284807 non-null float64 V9 284807 non-null float64 V10 284807 non-null float64 V11 284807 non-null float64 V12 284807 non-null float64 V13 284807 non-null float64 V14 284807 non-null float64 V15 284807 non-null float64 V16 284807 non-null float64 V17 284807 non-null float64 V18 284807 non-null float64 V19 284807 non-null float64 V20 284807 non-null float64 V21 284807 non-null float64 V22 284807 non-null float64 V23 284807 non-null float64 V24 284807 non-null float64 V25 284807 non-null float64 V26 284807 non-null float64 V27 284807 non-null float64 V28 284807 non-null float64 Amount 284807 non-null float64 Class 284807 non-null int64
# normalise the amount column data['normAmount'] = StandardScaler().fit_transform(np.array(data['Amount']).reshape(-1, 1)) # drop Time and Amount columns as they are not relevant for prediction purpose data = data.drop(['Time', 'Amount'], axis = 1) # as you can see there are 492 fraud transactions. data['Class'].value_counts()
Producción:
0 284315 1 492
Dividir los datos en conjuntos de prueba y entrenamiento
from sklearn.model_selection import train_test_split # split into 70:30 ration X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 0) # describes info about train and test set print("Number transactions X_train dataset: ", X_train.shape) print("Number transactions y_train dataset: ", y_train.shape) print("Number transactions X_test dataset: ", X_test.shape) print("Number transactions y_test dataset: ", y_test.shape)
Producción:
Number transactions X_train dataset: (199364, 29) Number transactions y_train dataset: (199364, 1) Number transactions X_test dataset: (85443, 29) Number transactions y_test dataset: (85443, 1)
Ahora entrene el modelo sin manejar la distribución de clases desequilibrada
# logistic regression object lr = LogisticRegression() # train the model on train set lr.fit(X_train, y_train.ravel()) predictions = lr.predict(X_test) # print classification report print(classification_report(y_test, predictions))
Producción:
precision recall f1-score support 0 1.00 1.00 1.00 85296 1 0.88 0.62 0.73 147 accuracy 1.00 85443 macro avg 0.94 0.81 0.86 85443 weighted avg 1.00 1.00 1.00 85443
La precisión resulta ser del 100%, pero ¿notaste algo extraño?
El recuerdo de la clase minoritaria en muy poco. Demuestra que el modelo está más sesgado hacia la clase mayoritaria. Por lo tanto, demuestra que este no es el mejor modelo.
Ahora, aplicaremos diferentes técnicas de manejo de datos desequilibrados y veremos su precisión y recuperaremos los resultados.
Uso del algoritmo SMOTE
Puedes consultar todos los parámetros desde aquí .
print("Before OverSampling, counts of label '1': {}".format(sum(y_train == 1))) print("Before OverSampling, counts of label '0': {} \n".format(sum(y_train == 0))) # import SMOTE module from imblearn library # pip install imblearn (if you don't have imblearn in your system) from imblearn.over_sampling import SMOTE sm = SMOTE(random_state = 2) X_train_res, y_train_res = sm.fit_sample(X_train, y_train.ravel()) print('After OverSampling, the shape of train_X: {}'.format(X_train_res.shape)) print('After OverSampling, the shape of train_y: {} \n'.format(y_train_res.shape)) print("After OverSampling, counts of label '1': {}".format(sum(y_train_res == 1))) print("After OverSampling, counts of label '0': {}".format(sum(y_train_res == 0)))
Producción:
Before OverSampling, counts of label '1': [345] Before OverSampling, counts of label '0': [199019] After OverSampling, the shape of train_X: (398038, 29) After OverSampling, the shape of train_y: (398038, ) After OverSampling, counts of label '1': 199019 After OverSampling, counts of label '0': 199019
¡Mirar! que el algoritmo SMOTE ha sobremuestreado las instancias minoritarias y las ha igualado a la clase mayoritaria. Ambas categorías tienen la misma cantidad de registros. Más específicamente, la clase minoritaria se ha incrementado al número total de la clase mayoritaria.
Ahora vea la precisión y recupere los resultados después de aplicar el algoritmo SMOTE (Sobremuestreo).
Predicción y recuerdo
lr1 = LogisticRegression() lr1.fit(X_train_res, y_train_res.ravel()) predictions = lr1.predict(X_test) # print classification report print(classification_report(y_test, predictions))
Producción:
precision recall f1-score support 0 1.00 0.98 0.99 85296 1 0.06 0.92 0.11 147 accuracy 0.98 85443 macro avg 0.53 0.95 0.55 85443 weighted avg 1.00 0.98 0.99 85443
Vaya , hemos reducido la precisión al 98 % en comparación con el modelo anterior, pero el valor de recuperación de la clase minoritaria también ha mejorado al 92 %. Este es un buen modelo en comparación con el anterior. Recordar es genial.
Ahora, aplicaremos la técnica NearMiss para submuestrear la clase mayoritaria y ver su precisión y resultados de recuperación.
Algoritmo de NearMiss:
Puedes consultar todos los parámetros desde aquí .
print("Before Undersampling, counts of label '1': {}".format(sum(y_train == 1))) print("Before Undersampling, counts of label '0': {} \n".format(sum(y_train == 0))) # apply near miss from imblearn.under_sampling import NearMiss nr = NearMiss() X_train_miss, y_train_miss = nr.fit_sample(X_train, y_train.ravel()) print('After Undersampling, the shape of train_X: {}'.format(X_train_miss.shape)) print('After Undersampling, the shape of train_y: {} \n'.format(y_train_miss.shape)) print("After Undersampling, counts of label '1': {}".format(sum(y_train_miss == 1))) print("After Undersampling, counts of label '0': {}".format(sum(y_train_miss == 0)))
Producción:
Before Undersampling, counts of label '1': [345] Before Undersampling, counts of label '0': [199019] After Undersampling, the shape of train_X: (690, 29) After Undersampling, the shape of train_y: (690, ) After Undersampling, counts of label '1': 345 After Undersampling, counts of label '0': 345
El algoritmo NearMiss submuestreó las instancias mayoritarias y las igualó a la clase mayoritaria. Aquí, la clase mayoritaria se ha reducido al número total de clases minoritarias, por lo que ambas clases tendrán el mismo número de registros.
Predicción y recuerdo
# train the model on train set lr2 = LogisticRegression() lr2.fit(X_train_miss, y_train_miss.ravel()) predictions = lr2.predict(X_test) # print classification report print(classification_report(y_test, predictions))
Producción:
precision recall f1-score support 0 1.00 0.56 0.72 85296 1 0.00 0.95 0.01 147 accuracy 0.56 85443 macro avg 0.50 0.75 0.36 85443 weighted avg 1.00 0.56 0.72 85443
Este modelo es mejor que el primer modelo porque clasifica mejor y además el valor de recuerdo de la clase minoritaria es del 95 %. Pero debido al submuestreo de la clase mayoritaria, su recuerdo ha disminuido al 56 %. Entonces, en este caso, SMOTE me está brindando una gran precisión y recuperación, ¡seguiré adelante y usaré ese modelo! 🙂
Publicación traducida automáticamente
Artículo escrito por tyagikartik4282 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA