[PYTHON] Prédiction de probabilité de données déséquilibrées

introduction

Lors de la classification avec l'apprentissage automatique, vous souhaiterez peut-être obtenir la probabilité d'appartenir à ces classes ainsi que les résultats de la classification. Si le nombre de données positives est extrêmement petit par rapport au nombre de données négatives (ces données sont appelées données déséquilibrées), si vous créez un modèle de prédiction utilisant toutes ces données, le résultat de la prédiction sera également négatif. Dans de nombreux cas, il a tendance à être difficile de classer avec précision les données positives. Par conséquent, dans de nombreux cas, un modèle est construit à l'aide de données sous-échantillonnées de sorte que le nombre d'exemples de données négatives est égal au nombre d'exemples de données positives. Cela permet de classer avec précision les données d'exemple positives, mais comme l'équilibre entre le nombre de données positives et négatives est différent des données d'origine, le résultat de la prédiction de probabilité est biaisé en raison du sous-échantillonnage. Je vais finir.

Certaines personnes ont déjà résumé comment traiter ce problème dans le blog suivant, etc., mais je résumerai sous forme de mémorandum comment supprimer et corriger le biais de la sortie de probabilité par le modèle construit avec des données sous-échantillonnées. .. Dans cet article, nous utiliserons simplement le modèle de régression logistique comme modèle de prédiction probabiliste.

Comment corriger le biais dû au sous-échantillonnage

La méthode de correction du biais par sous-échantillonnage est décrite dans l'article [Étalonnage de la probabilité avec sous-échantillonnage pour une classification déséquilibrée]. Proposé sur (https://www3.nd.edu/~dial/publications/dalpozzolo2015calibrating.pdf).

Considérons maintenant une tâche de classification binaire qui prédit la variable objectif $ Y $ ($ Y $ prend 0 ou 1) à partir de la variable explicative $ X $. L'ensemble de données d'origine $ (X, Y) $ est des données déséquilibrées avec un nombre extrêmement petit d'exemples positifs, et l'ensemble de données dans lequel le nombre d'exemples négatifs est égal au nombre d'exemples positifs par sous-échantillonnage est $ (X_s). , Y_s) $. De plus, si des données (échantillon) contenues dans $ (X, Y) $ sont également contenues dans $ (X_s, Y_s) $, elles prennent 1 et 0 si elles ne sont pas incluses dans $ (X_s, Y_s) $. Introduit une variable d'échantillonnage $ s $ qui prend.

Jeu de données d'origine(X, Y)Lorsqu'une variable explicative est donnée à un modèle construit en utilisant, la probabilité de la prédire comme un exemple positif estp(y=1|x)Il est exprimé comme. Aussi, jeux de données sous-échantillonnés(X_s, Y_s)Lorsqu'une variable explicative est donnée à un modèle construit en utilisant, la probabilité de la prédire comme un exemple positif estp(y=1|x,s=1)Il est exprimé comme.p=p(y=1|x), p_s=p(y|x,s=1)PuispQuandp_sLa relation de est la suivante.

p=\frac{\beta p_s}{\beta p_s-p_s+1}

Ici, $ \ beta = N ^ + / N ^ - $ ($ N ^ + $ est le nombre de données positives, $ N ^ - $ est le nombre de données négatives).

Dérivation

Ce qui suit est une explication détaillée de la formule, donc si vous n'êtes pas intéressé, veuillez l'ignorer.

Théorème de Bayes, etp(s|y,x)=p(s|y)À partir de l'ensemble de données sous-échantillonné(X_s, Y_s)La probabilité prédite par le modèle construit à l'aide de s'écrit comme suit.

p(y=1|x,s=1)=\frac{p(s=1|y=1)p(y=1|x)}{p(s=1|y=1)p(y=1|x)+p(s=1|y=0)p(y=0|x)}

Maintenant, le nombre de données régulières est extrêmement petit, et toutes les données pour lesquelles $ y = 1 $ sont échantillonnées, donc si $ p (s = 1 | y = 1) = 1 $,

p(y=1|x,s=1)=\frac{p(y=1|x)}{p(y=1|x)+p(s=1|y=0)p(y=0|x)}

Peut être écrit. plus loin,p=p(y=1|x), p_s=p(y|x,s=1), \beta=p(s=1|y=0)Puis

p_s=\frac{p}{p+\beta(1-p)}

Ce sera. Enfin, lorsque $ p $ est transformé pour qu'il soit sur le côté gauche,

p=\frac{\beta p_s}{\beta p_s-p_s+1}

Ce sera. La dernière équation signifie que le modèle construit avec les données sous-échantillonnées prédira la probabilité $ p_s $ en corrigeant le biais et le modèle construit avec les données d'origine prédira la probabilité $ Cela signifie que vous pouvez calculer p $.

Où $ \ beta = p (s = 1 | y = 0) $ représente la probabilité que des exemples de données négatives soient échantillonnés. Maintenant, comme les données d'exemple négatives sont échantillonnées dans le même nombre que les données d'exemple positives, $ \ beta = N ^ + / N ^ - $ ($ N ^ + $ est le nombre d'exemples de données positives, $ N ^ - $ est Il peut être approximé au nombre de données dans l'exemple négatif).

Exemple de code

Dans ce qui suit, nous allons effectuer une expérience pour corriger la probabilité de prédiction tout en montrant un exemple de code réel. (L'environnement d'exploitation du code suivant est Python 3.7.3, pandas 0.24.2, scikit-learn 0.20.3.)

L'expérience est réalisée selon le flux suivant.

  1. Construisez un modèle en utilisant les données déséquilibrées telles quelles et vérifiez l'exactitude de la classification.
  2. Construisez un modèle en utilisant des données sous-échantillonnées et vérifiez que la précision de la classification est améliorée, mais que la précision de la prédiction de probabilité est faible.
  3. Vérifiez si la précision de la prédiction de probabilité peut être améliorée en appliquant une correction qui supprime le biais dû au sous-échantillonnage.

Ici, le [Adult Dataset](http: //archive.ics.uci.) Publié dans le UCI Machine Learning Repository. Utilisez edu / ml / machine-learning-databases / adult /). Cet ensemble de données est un ensemble de données permettant de classer si le revenu annuel d'un individu est de 50 000 $ ou plus en fonction de données telles que le sexe et l'âge.

Commencez par charger les données à utiliser. Ici, dans Adult Dataset Enregistrez adult.data et adult.test localement sous forme de fichiers CSV, et utilisez le premier comme données d'entraînement et le second comme données de vérification.

import numpy as np
import pandas as pd

#Lire les données
train_data = pd.read_csv('./adult_data.csv', names=['age', 'workclass', 'fnlwgt', 'education', 'education-num', 
                                         'marital-status', 'occupation', 'relationship', 'race', 'sex', 
                                         'capital-gain', 'capital-loss', 'hours-per-week', 'native-country', 'obj'])
test_data = pd.read_csv('./adult_test.csv', names=['age', 'workclass', 'fnlwgt', 'education', 'education-num', 
                                         'marital-status', 'occupation', 'relationship', 'race', 'sex', 
                                         'capital-gain', 'capital-loss', 'hours-per-week', 'native-country', 'obj'],
                       skiprows=1)
data = pd.concat([train_data, test_data])

#Variable explicative X,Traitement de la variable objective Y
X = pd.get_dummies(data.drop('obj', axis=1))
Y = data['obj'].map(lambda x: 1 if x==' >50K' or x==' >50K.' else 0) #La variable objective est 1 ou 0

#Divisé en données d'entraînement et données de vérification
train_size = len(train_data)
X_train, X_test = X.iloc[:train_size], X.iloc[train_size:] 
Y_train, Y_test = Y.iloc[:train_size], Y.iloc[train_size:]

En regardant le pourcentage de cas positifs dans les données d'entraînement, il est d'environ 24%, ce qui est inférieur à celui des cas négatifs, et on peut dire qu'il s'agit de données déséquilibrées.

print('positive ratio = {:.2f}%'.format((len(Y_train[Y_train==1])/len(Y_train))*100))
#production=> positive ratio = 24.08%

Si vous créez un modèle en utilisant ces données d'entraînement telles quelles, vous pouvez voir que la précision de classification est aussi faible que AUC = 0,57 et le taux de rappel (rappel) est aussi bas que 0,26. On considère que le nombre d'exemples négatifs dans les données d'apprentissage est important, que le résultat de la prédiction est souvent négatif et que le taux de rappel (le taux auquel les données positives peuvent être correctement classées comme positives) est faible.

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score, recall_score

#Construction de modèles
lr = LogisticRegression(random_state=0)
lr.fit(X_train, Y_train)

#Vérifier l'exactitude de la classification
prob = lr.predict_proba(X_test)[:, 1] #Prédire la probabilité que la variable objective soit 1
pred = lr.predict(X_test) #Classé 1 ou 0
auc = roc_auc_score(y_true=Y_test, y_score=prob)
print('AUC = {:.2f}'.format(auc))
recall = recall_score(y_true=Y_test, y_pred=pred)
print('recall = {:.2f}'.format(recall))

#production=> AUC = 0.57
#production=> recall = 0.26

Ensuite, le sous-échantillonnage est effectué afin que le nombre d'exemples négatifs dans les données d'apprentissage soit égal au nombre d'exemples positifs, et lorsqu'un modèle est construit à l'aide de ces données, la précision de la classification est grandement améliorée à AUC = 0,90 et rappel = 0,86. Tu peux voir ça

#Sous-échantillonnage
pos_idx = Y_train[Y_train==1].index
neg_idx = Y_train[Y_train==0].sample(n=len(Y_train[Y_train==1]), replace=False, random_state=0).index
idx = np.concatenate([pos_idx, neg_idx])
X_train_sampled = X_train.iloc[idx]
Y_train_sampled = Y_train.iloc[idx]

#Construction de modèles
lr = LogisticRegression(random_state=0)
lr.fit(X_train_sampled, Y_train_sampled)

#Vérifier l'exactitude de la classification
prob = lr.predict_proba(X_test)[:, 1]
pred = lr.predict(X_test)
auc = roc_auc_score(y_true=Y_test, y_score=prob)
print('AUC = {:.2f}'.format(auc))
recall = recall_score(y_true=Y_test, y_pred=pred)
print('recall = {:.2f}'.format(recall))

#production=> AUC = 0.90
#production=> recall = 0.86

À ce stade, regardons la précision de prédiction de la probabilité. Vous pouvez voir que la perte de journal est de 0,41 et le tracé d'étalonnage passe en dessous de la ligne à 45 degrés. Le fait que le tracé d'étalonnage passe en dessous de la ligne de 45 degrés signifie que la probabilité prédite est supérieure à la probabilité réelle. Maintenant, puisque le modèle est construit en utilisant des données sous-échantillonnées de sorte que le nombre de données d'exemple négatives est égal au nombre d'exemples de données positives, l'apprentissage est effectué avec le rapport du nombre de données d'exemple positives plus grand que le nombre réel. On pense que la probabilité est assez élevée.

import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.calibration import calibration_curve
from sklearn.metrics import  log_loss

def calibration_plot(y_true, y_prob):
    prob_true, prob_pred = calibration_curve(y_true=y_true, y_prob=y_prob, n_bins=20)

    fig, ax1 = plt.subplots()
    ax1.plot(prob_pred, prob_true, marker='s', label='calibration plot', color='skyblue') #Créer un tracé d'étalonnage
    ax1.plot([0, 1], [0, 1], linestyle='--', label='ideal', color='limegreen') #Tracez la ligne à 45 degrés
    ax1.legend(bbox_to_anchor=(1.12, 1), loc='upper left')
    plt.xlabel('predicted probability')
    plt.ylabel('actual probability')
    
    ax2 = ax1.twinx() #Ajout de 2 axes
    ax2.hist(prob, bins=20, histtype='step', color='orangered') #Tracé avec histogramme de score
    plt.ylabel('frequency')
    plt.show()

prob = lr.predict_proba(X_test)[:, 1]
loss = log_loss(y_true=Y_test, y_pred=prob)
print('log loss = {:.2f}'.format(loss))
calibration_plot(y_true=Y_test, y_prob=prob)

#production=> log loss = 0.41

image.png

Maintenant, supprimons le biais dû au sous-échantillonnage et corrigeons la probabilité. Calculez $ \ beta $ et $ Si vous corrigez la probabilité en fonction de p = \ beta p_s / (\ beta p_s-p_s + 1 $), vous pouvez voir que la perte de log s'est améliorée à 0,32 et que le tracé d'étalonnage était presque sur la ligne de 45 degrés. Notez que $ \ beta $ utilise le nombre d'exemples positifs / négatifs de données d'entraînement (le nombre d'exemples positifs / négatifs de données de vérification est inconnu).

beta = len(Y_train[Y_train==1]) / len(Y_train[Y_train==0])
prob_corrected = beta*prob / (beta*prob - prob + 1)

loss = log_loss(y_true=Y_test, y_pred=prob_corrected)
print('log loss = {:.2f}'.format(loss))
calibration_plot(y_true=Y_test, y_prob=prob_corrected)

#production=> log loss = 0.32

image.png

Il a été confirmé que le biais dû au sous-échantillonnage pouvait être éliminé et que la probabilité pouvait être corrigée. C'est tout pour la vérification.

en conclusion

Dans cet article, j'ai brièvement résumé comment corriger la probabilité prédite par un modèle construit à l'aide de données sous-échantillonnées. Je vous serais reconnaissant si vous pouviez signaler des erreurs.

référence

Recommended Posts

Prédiction de probabilité de données déséquilibrées
Échantillonnage dans des données déséquilibrées
Pré-traitement des données préfectorales
Sélection des données de mesure
Expérience de réglage des données Tensorflow
Visualisation des données par préfecture
Prédiction de la moyenne Nikkei avec Pytorch 2
Transformée de Fourier des données brutes
Estimation moyenne des données plafonnées
Prédiction de la moyenne Nikkei avec Pytorch
La probabilité de précipitation est-elle correcte?
Comment gérer les données déséquilibrées
Concours de prédiction de données en 3 étapes (titanesque)
Conversion matricielle d'économie de mémoire des données de journal
Prédiction de l'onde de péché avec keras
Différenciation des données de séries chronologiques (discrètes)
10 sélections d'extraction de données par pandas.DataFrame.query
Animation des géodonnées par geopandas
Recommandation d'analyse des données à l'aide de MessagePack
Analyse des séries chronologiques 3 Prétraitement des données des séries chronologiques
Traitement des données 2 Analyse de divers formats de données
4/22 prédiction de l'onde de péché avec keras
Résumé des distributions de probabilité qui apparaissent souvent dans les statistiques et l'analyse des données
Python: prétraitement en machine learning: gestion des données manquantes / aberrantes / déséquilibrées