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.
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
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).
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, et
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_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).
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.
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
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
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.
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.
Recommended Posts