[PYTHON] Quels sont les facteurs à l'origine de la mauvaise prédiction du ML? ~ La recherche factorielle est décidée par arbre ~

Je souhaite améliorer la précision du modèle de prédiction

Mais que dois-je faire ... Quelle est la cause de l'erreur de prédiction en premier lieu? ?? Oui, pouvons-nous visualiser la cause de l'erreur de prédiction avec un arbre de décision? Tentative. (Bien qu'il puisse y avoir des erreurs théoriques) Je vais publier le flux de l'analyse, donc je vais écrire sur le traitement des données et ainsi de suite. J'écrirai dans le flux de lecture de données, de prétraitement, de confirmation de données, de construction de modèle / confirmation de précision, d'enquête factorielle. Peut-être que ce sera plus long. Le sujet principal est ce chapitre → [Visualisation des caractéristiques clients imprévisibles dans l'arbre de décision](# Visualisation des caractéristiques clients imprévisibles dans l'arbre de décision)

Des données d'utilisation

Utilisez les données de Churn de clients de télécommunications de Kaggle. https://www.kaggle.com/blastchar/telco-customer-churn Il s'agit de données sur les clients de la compagnie de téléphone et il s'agit d'un problème de classification binaire, la variable objective étant d'annuler ou non. Chaque ligne représente un client et chaque colonne contient les attributs du client. Chaque colonne est la suivante. (Traduction Google de la description de la colonne kaggle)

customerID: numéro de client
sexe: si le client est un homme ou une femme
SeniorCitizen: si le client est âgé (1, 0)
Partenaire: si le client a un partenaire (oui, non)
Personnes à charge: si le client a des personnes à charge (oui, non)
ancienneté: le nombre de mois pendant lesquels le client est resté dans l'entreprise
PhoneService: si vous utilisez le service téléphonique (oui, non)
MultipleLines: si le client dispose de plusieurs lignes (oui, non, pas de service téléphonique)
InternetService: fournisseur d'accès Internet du client (DSL, fibre optique, non)
OnlineSecurity: si vous avez une sécurité en ligne (oui, non, pas de service Internet)
OnlineBackup: si vous avez une sauvegarde en ligne (oui, non, pas de service Internet)
DeviceProtection: si vous protégez votre appareil (oui, non, pas de service Internet)
TechSupport: si vous avez un support technique (oui, non, pas de service Internet)
StreamingTV: si vous avez une TV en streaming (oui, non, pas de service Internet)
StreamingMovies: si le client a des films en streaming (oui, non, pas de service Internet)
Contrat: durée du contrat du client (mensuel, 1 an, 2 ans)
Facturation sans papier: si vous faites une facture sans papier (oui, non)
Mode de paiement: Mode de paiement client (chèque électronique, chèque postal, virement bancaire (automatique), carte de crédit (automatique))
Frais mensuels: Montant facturé aux clients chaque mois
TotalCharges: montant total facturé au client

Churn: si le client a annulé (oui ou non)

Aperçu des données

Jetons un coup d'œil rapide au contenu des données.

Informations de base

#Importation de package
import pandas as pd
from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
from sklearn import preprocessing
from sklearn import tree
from sklearn.externals.six import StringIO
import pydotplus
from IPython.display import Image
from dtreeviz.trees import dtreeviz
import xgboost.sklearn as xgb
plt.style.use('seaborn-darkgrid')
%matplotlib inline

#Lecture des données
df=pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv')
churn=df.copy()
print(churn.info())
display(churn.head())

image.png

Ligne manquante

Changer en valeur manquante

La colonne TotalCharges est un nombre, mais ce n'est pas un type de nombre car il y a des lignes vides. Convertissez donc le blanc en Nan et convertissez-le en type numérique.

#Changer le blanc demi-largeur en Nan
churn.loc[churn['TotalCharges']==' ', 'TotalCharges']=np.nan
#Changer pour flotter
churn['TotalCharges']=churn['TotalCharges'].astype(float)
print(churn.info())
display(churn.head())

image.png Conversion terminée de la colonne Total des frais en flottant.

Fin de ligne manquante

Ensuite, travaillez pour compléter Nan of Total Charges. À l'heure actuelle, il semble que le remplissage de Nan avec la variable catégorielle avec le plus grand biais de Total Charges en fonction de la catégorie soit plus efficace pour la prédiction que de simplement remplir Nan avec la valeur moyenne de Total Charges. Par conséquent, la politique est de remplir Nan avec la valeur moyenne des frais totaux pour chaque variable de catégorie. Tout d'abord, créez un bloc de données à l'exclusion de Nan, puis créez un bloc de données avec uniquement des variables catégorielles et des frais totaux.

#Création d'un bloc de données avec les lignes Nan supprimées
churn2=churn[churn.isnull().any(axis=1)==False].copy()
churn2['TotalCharges']=churn2['TotalCharges'].astype(float)

#Création d'un bloc de données de variable de catégorie + Total des frais
#Extraire uniquement les données dont dtype est de type objet
churn2_TotalCharges=churn2.select_dtypes(include=['object']).drop('customerID',axis=1)
#Frais totaux ajoutés
churn2_TotalCharges['TotalCharges']=churn2['TotalCharges']
print(churn2_TotalCharges.info())

image.png En raison de l'exclusion des lignes Nan, le nombre est passé de 7 043 à 7 032. Ensuite, visualisons quelles variables catégorielles sont susceptibles d'être liées au total des frais.

#Définir une liste de noms de variables de catégorie sans frais totaux
obj_columns=churn2_TotalCharges.columns.values
obj_columns=obj_columns[~(obj_columns == 'TotalCharges')]

#Dessinez un histogramme des frais totaux pour chaque variable de catégorie
fig=plt.figure(figsize=(12,10))
for i in range(len(obj_columns)):
    col=obj_columns[i]
    youso=churn2_TotalCharges[col].unique()
    ax=plt.subplot(round(len(obj_columns)/np.sqrt(len(obj_columns))), round(len(obj_columns)/np.sqrt(len(obj_columns))), i+1)
    for j in range(len(youso)):
        sns.distplot(churn2_TotalCharges[churn2_TotalCharges[col]==youso[j]]['TotalCharges'], bins=30, ax=ax, kde=False, label=youso[j])
    ax.legend(loc='upper right')
    ax.set_ylabel('Freq')
    ax.set_title(col)
plt.tight_layout()
fig.suptitle("TotalCharges hist", fontsize=15)
plt.subplots_adjust(top=0.92)
plt.show()

image.png 'MultipleLines', 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies' → On a le sentiment que la répartition des charges totales diffère grandement pour chaque catégorie dans les variables de catégorie dans ce domaine. À partir de là, vous pouvez sélectionner une variable catégorielle qui calcule la moyenne des frais totaux, mais si possible, j'aimerais faire un jugement quantitatif.

Ensuite, bien que ce soit différent du sujet principal, j'essaierai de juger en utilisant ici aussi l'arbre de décision. CART crée une branche de variable explicative qui sépare au mieux la variable objective basée sur l'impureté de gini (ou l'entropie) et le gain d'information. Pour une histoire théorique, voir ["Première reconnaissance de formes"](https://www.amazon.co.jp/dp/4627849710 "" Première reconnaissance de formes "") ou le site suivant. https://dev.classmethod.jp/articles/2017ad_20171211_dt-2/

En d'autres termes, si CART est appliqué à TotalCharges en tant que variable objectif, la variable explicative qui sépare le mieux TotalCharges doit arriver à la première branche. Je vais vraiment l'essayer.

#Copier un nouveau bloc de données
churn2_TotalCharges_trans=churn2_TotalCharges.copy()
#Étiquette de codage des variables catégorielles
for column in churn2_TotalCharges_trans.columns:
    le = preprocessing.LabelEncoder()
    le.fit(churn2_TotalCharges_trans[column])
    churn2_TotalCharges_trans[column] = le.transform(churn2_TotalCharges_trans[column])

display(churn2_TotalCharges_trans)

image.png

#Construction d'un modèle d'arbre de décision
clf = tree.DecisionTreeRegressor(max_depth=1)
#Base de données avec uniquement des variables catégorielles comme variable explicative, Total des charges comme variable objective
clffit = clf.fit(churn2_TotalCharges_trans.drop('TotalCharges',axis=1).values\
                 , churn2_TotalCharges_trans['TotalCharges'].values)

#Visualisation de l'arbre de décision
dot_data = StringIO()
tree.export_graphviz(clffit, out_file=dot_data,\
                     feature_names=churn2_TotalCharges_trans.drop('TotalCharges',axis=1).columns.values,\
                     class_names=True,\
                     filled=True, rounded=True)

graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
Image(graph.create_png())

image.png Puisque «Protection de l'appareil» est la variable qui divise le plus TotalCharges, remplissez Nan avec la valeur moyenne de TotalCharges pour chaque catégorie de cette variable.

#Calculez la moyenne des frais totaux pour chaque protection d'appareil
churn2_TotalCharges_mean=churn2.groupby(['DeviceProtection'])[['TotalCharges']].mean().reset_index().rename(columns={'TotalCharges':'TotalCharges_mean'})
#Fusionner les charges totales moyennes par protection de périphérique dans une trame de données contenant Nan
churn=pd.merge(churn,churn2_TotalCharges_mean,on=['DeviceProtection'],how='left')
#Complément Nan
churn.loc[(churn.isnull().any(axis=1)==True), 'TotalCharges']=churn['TotalCharges_mean']
#Colonne moyenne des frais totaux supprimée
churn=churn.drop('TotalCharges_mean',axis=1)
#Confirmation de la ligne Nan
display(churn[churn.isnull().any(axis=1)==True])

Nan a été complété. image.png

Vue d'ensemble des données

Puisque les valeurs manquantes ont disparu et que les données sont complétées, nous effectuerons une vue d'ensemble des variables de valeur continue et des variables catégorielles.

# Churn:Graphique circulaire du ratio d'annulation Oui ou Non
pie=churn.groupby(['Churn'])[['customerID']].count().reset_index()
fig=plt.figure(figsize=(8,8))
ax=plt.subplot(1,1,1)
texts = ax.pie(pie['customerID'], labels=pie['Churn'], counterclock=False, startangle=90, autopct="%1.1f%%")
for t,t2 in texts[1],texts[2]:
    t.set_size(18)
    t2.set_size(18)
ax.set_title('Churn ratio', fontsize=20)
plt.show()

image.png

#Créer un bloc de données en extrayant uniquement des variables continues
churn_select=churn.select_dtypes(include=['number']).copy()
#Extraire uniquement les variables catégorielles et créer un bloc de données (utilisé plus tard)
churn_select_obj=churn.select_dtypes(include=['object']).copy()
churn_select_obj['SeniorCitizen']=churn_select['SeniorCitizen']
#Ajouter une variable d'objectif
churn_select['Churn']=churn['Churn']
churn_select=churn_select.drop('SeniorCitizen',axis=1)
# label encoding
le = preprocessing.LabelEncoder()
le.fit(churn_select['Churn'])
# Churn Yes:1、No:0
churn_select['Churn'] = le.transform(churn_select['Churn'])
#Diagramme de paires variable continu
sns.pairplot(data=churn_select, hue='Churn', diag_kind='hist',plot_kws={'marker': '+', 'alpha': 0.5},diag_kws={'bins': 20})
plt.show()

image.png

#Liste des noms de variables de catégorie
col=churn_select_obj.columns.values[1:]
col2=col[~(col == 'Churn')]
#Churn à axe horizontal, graphique à barres de comptage à axe vertical pour chaque catégorie
fig=plt.figure(figsize=(10,10))
churn_num=churn.copy()
churn_num['num']=1
for i in range(len(col2)):
    ax=plt.subplot(round(len(col2)/np.sqrt(len(col2))),round(len(col2)/np.sqrt(len(col2))),i+1)
    sns.barplot(data=churn_num.groupby(['Churn',col2[i]])[['num']].count().reset_index(),x='Churn',y='num',hue=col2[i],ax=ax)
    ax.legend(loc='upper right')
    ax.set_ylabel('count')
    ax.set_title(col2[i]+' count')
plt.tight_layout()
plt.show()

image.png Je pense que ce sont des données un peu déséquilibrées, que celui qui a une ancienneté plus longue et des charges totales a tendance à ne pas annuler, que si le contrat est payé annuellement, il n'y a pas beaucoup d'annulateurs et le sexe semble être sans importance.

Construction de modèles

Maintenant que j'ai vu les données, faisons un modèle.

#Désabonnement de trame de données avec étiquette_faire encoder
churn_encode=churn.copy()
columns=list(churn_encode.select_dtypes(include=['object']).columns.values)
for column in columns:
    le = preprocessing.LabelEncoder()
    le.fit(churn_encode[column])
    churn_encode[column] = le.transform(churn_encode[column])
churn_encode=churn_encode.drop('customerID',axis=1)
# Train,Fonction pour créer des données de test
def createXy(df, col, target, test_size=0.3, random_state=0):
    #Séparation des variables explicatives et des variables objectives
    dfx=df[col]
    dfy=df[target]
    X_train, X_test, y_train, y_test = train_test_split(dfx, dfy, test_size=test_size, random_state=random_state)
    print('TrainX Size is {}'.format(X_train.shape))
    print('TestX Size is {}'.format(X_test.shape))
    print('TrainY Size is {}'.format(y_train.shape))
    print('TestY Size is {}'.format(y_test.shape))
    return X_train, y_train, X_test, y_test

Cette fois, c'est gênant, je n'ai donc pas eu à sélectionner la quantité de fonctionnalités ou à ajuster le para haut.

#Nom de la variable explicative
colx=churn_encode.columns.values[:-1]
#Nom de la variable d'objectif
coly='Churn'
X_train, y_train, X_test, y_test = createXy(churn_encode, colx, coly, test_size=0.3, random_state=0)

#Créer un modèle avec XGBoost
xgb_model = xgb.XGBClassifier()
xgb_model.fit(X_train, y_train)
print('Train accuracy_score',xgb_model.score(X_train, y_train))
y_true=y_test.values
y_pred=xgb_model.predict(X_test)
#Vérification de l'exactitude des données de test
print('Test accuracy_score',accuracy_score(y_true, y_pred))
print('Test precision_score',precision_score(y_true, y_pred))
print('Test recall_score',recall_score(y_true, y_pred))
print('Test f1_score',f1_score(y_true, y_pred))

résultat: Train accuracy_score 0.8267748478701825

Test accuracy_score 0.7946048272598202 Test precision_score 0.6307692307692307 Test recall_score 0.5189873417721519 Test f1_score 0.5694444444444443

#Voir aussi Matrice de confusion
result=pd.DataFrame({'y_true':y_true,'y_pred':y_pred})
result['dummy']=1
display(result.pivot_table(result,index='y_true',columns='y_pred',aggfunc='count').fillna(0))

1: annulé, 0: non annulé image.png

À propos, la confirmation de la précision du modèle est enfin terminée. Regardons la cause de l'erreur de prédiction du prochain.

Visualisation des caractéristiques clients imprévisibles dans l'arbre de décision

Enfin le sujet principal. Quelles sont les caractéristiques du client qui n'a pas été prédit? J'aimerais voir cela dans l'arbre de décision. Comme mentionné ci-dessus, l'algorithme CART de l'arbre de décision génère une branche de la variable explicative qui sépare le mieux la variable objectif basée sur la pureté gini (ou l'entropie) et le gain d'information. Par conséquent, si vous dessinez un arbre de décision avec les deux valeurs que la prédiction est correcte ou incorrecte comme variable objectif, il semble que vous puissiez générer une branche de la variable explicative qui sépare le mieux la prédiction d'être correcte ou incorrecte. Tout d'abord, créez une colonne indiquant que la prédiction était correcte ou incorrecte.

# X_Faites une copie du test et ajoutez une nouvelle variable
churn_test=X_test.copy()
churn_test['true']=y_true
churn_test['pred']=y_pred
churn_test['RightOrWrong']=0
# y_vrai et y_RightOrWrong si pred correspond=Définir sur 1
churn_test.loc[(churn_test['true']==churn_test['pred']),'RightOrWrong']=1
display(churn_test.groupby(['RightOrWrong'])[['pred']].count())
display(churn_test)

434 clients imprévisibles sur 2113 image.png Visualisons le type de tendance de ces 434 personnes avec un arbre de décision.

#Profondeur d'arbre 3 (texto)
clf = tree.DecisionTreeClassifier(max_depth=3)
# 'true','pred','RightOrWrong'Variables explicatives sans'RightOrWrong'La variable objective
clffit = clf.fit(churn_test.drop(['true','pred','RightOrWrong'],axis=1), churn_test['RightOrWrong'])

#Visualisé avec dtreeviz
viz = dtreeviz(\
    clffit,\
    churn_test.drop(['true','pred','RightOrWrong'],axis=1),\
    churn_test['RightOrWrong'],\
    target_name='RightOrWrong',\
    feature_names=churn_test.drop(['true','pred','RightOrWrong'],axis=1).columns.values,\
    class_names=[0,1], histtype='bar'\
) 
print('Data Count ',len(churn_test))
display(viz)

Data Count 2113 image.png Apparemment, le contrat est au sommet de l'arbre, il semble donc être la variable explicative qui distingue le mieux les prédictions de succès et d'échec. Le contrat est 0 = les personnes qui paient mensuellement ont tendance à se tromper Au contraire, 1 = paiement annuel et 2 = paiement sur 2 ans semblent être corrects dans une proportion considérable. Parmi ceux qui paient mensuellement, ceux qui ont le service Internet 2 = Non ont tendance à avoir raison, et ceux qui ont 0 = DSL et 1 = Fibre optique sont hors de question. Jetons un coup d'œil au graphique qui a été montré plus tôt. image.png Les personnes qui paient des contrats mensuels ont de nombreux Oui et Non en Churn, et ceux qui utilisent le Service Internet avec la fibre optique ont beaucoup de Oui et Non en Churn. (Les gens qui utilisent les services Internet ont tendance à annuler et ils sont insatisfaits parce que la ligne est lente, non?) Il semble que ces personnes ont tendance à rater la prédiction parce qu'il est difficile de voir les tendances Oui et Non de Churn, et du fait de la visualisation réelle avec l'arbre de décision, il s'est avéré que la prédiction a tendance à être fausse. TotalCharges apparaît également dans l'arborescence, mais il semble que la tendance à la précision des prévisions ne changera pas lorsque la valeur augmente dans une certaine mesure. Pour le moment, sur la base de ces résultats, ajoutez des variables synthétiques et des variables telles que le découpage, créez à nouveau un modèle et vérifiez la précision.

fig=plt.figure(figsize=(10,10))
ax1=plt.subplot(2,2,1)
ax2=plt.subplot(2,2,2)
ax3=plt.subplot(2,2,3)
ax4=plt.subplot(2,2,4)

#Histogramme des charges totales
ax1.hist(X_train['TotalCharges'].values,bins=20)
ax1.set_title('X_train TotalCharges')
ax2.hist(X_test['TotalCharges'].values,bins=20)
ax2.set_title('X_test TotalCharges')

#Frais totaux d'écrêtage
upperbound, lowerbound = np.percentile(X_train['TotalCharges'].values, [0, 90])
TotalCharges_train = np.clip(X_train['TotalCharges'].values, upperbound, lowerbound)
ax3.hist(TotalCharges_train,bins=20)#Histogramme des charges totales
ax3.set_title('X_train TotalCharges clipping')

#Frais totaux d'écrêtage
upperbound, lowerbound = np.percentile(X_test['TotalCharges'].values, [0, 90])
TotalCharges_test = np.clip(X_test['TotalCharges'].values, upperbound, lowerbound)
ax4.hist(TotalCharges_test,bins=20)#Histogramme des charges totales
ax4.set_title('X_test TotalCharges clipping')
plt.show()

image.png

#Nouveau train avec variables,Faire un test
new_X_train=X_train.copy()
new_X_test=X_test.copy()
#Remplacer les frais totaux par le découpage
new_X_train['TotalCharges']=TotalCharges_train
new_X_test['TotalCharges']=TotalCharges_test
#Créer une variable composite pour le contrat et le service Internet
new_X_train['Contract_InternetService']=new_X_train['Contract'].astype(str)+new_X_train['InternetService'].astype(str)
new_X_test['Contract_InternetService']=new_X_test['Contract'].astype(str)+new_X_test['InternetService'].astype(str)
#LabelEncodage de la variable composite du contrat et du service Internet
le = preprocessing.LabelEncoder()
le.fit(new_X_train['Contract_InternetService'])
new_X_train['Contract_InternetService'] = le.transform(new_X_train['Contract_InternetService'])
le = preprocessing.LabelEncoder()
le.fit(new_X_test['Contract_InternetService'])
new_X_test['Contract_InternetService'] = le.transform(new_X_test['Contract_InternetService'])
#Construire un modèle
xgb_model = xgb.XGBClassifier()
xgb_model.fit(new_X_train, y_train)
print('Train accuracy_score',xgb_model.score(new_X_train, y_train))
y_true=y_test.values
y_pred=xgb_model.predict(new_X_test)
#Vérification de l'exactitude
print('')
print('Test accuracy_score',accuracy_score(y_true, y_pred))
print('Test precision_score',precision_score(y_true, y_pred))
print('Test recall_score',recall_score(y_true, y_pred))
print('Test f1_score',f1_score(y_true, y_pred))

#Matrice de confirmation de confusion
result=pd.DataFrame({'y_true':y_true,'y_pred':y_pred})
result['dummy']=1
display(result.pivot_table(result,index='y_true',columns='y_pred',aggfunc='count').fillna(0))

résultat: Train accuracy_score 0.8253549695740365

Test accuracy_score 0.7998106956933271 Test precision_score 0.6406926406926406 Test recall_score 0.5352622061482821 Test f1_score 0.5832512315270936

Premier résultat (republication): Train accuracy_score 0.8267748478701825

Test accuracy_score 0.7946048272598202 Test precision_score 0.6307692307692307 Test recall_score 0.5189873417721519 Test f1_score 0.5694444444444443

Matrice confuse 1: annulé, 0: non annulé résultat: image.png Premier résultat (republication): image.png Amélioration de la précision des données de test dans toutes les métriques.

Résumé

La lecture des données, le prétraitement, la confirmation des données, la construction du modèle / la confirmation de l'exactitude et l'investigation factorielle des erreurs de prédiction sont tous décrits. Le sujet principal de cet article n'est-il pas de pouvoir enquêter sur les facteurs qui rendent la prédiction erronée avec l'arbre de décision? J'ai pratiqué l'idée. Je suis désolé si la théorie ou l'interprétation est erronée. J'ai pensé que cela pourrait être utile pour signaler les problèmes actuels du modèle de prédiction à des clients tels que PoC. Tout en montrant la courbe d'apprentissage, j'ai également montré le résultat de l'arbre de décision, comme "Je n'ai peut-être pas assez de données" ou "Je pense que je devrais le gérer comme ça parce que la prédiction est erronée ici." Il est également bon de discuter avec les clients. Dans une telle histoire, il semble qu'il y ait des situations où les clients possédant une connaissance abondante du domaine peuvent aider en pensant: "Au fait, il peut y avoir de telles données et elles peuvent être ajoutées à des variables?" L'arbre de décision est facile à voir! Il est normal de mettre le prix SHAP, mais cela peut être difficile à comprendre pour les clients et c'est difficile à expliquer.

Bonus (SHAP)

Vérifions la tendance des clients lorsque la prédiction est fausse même avec la valeur SHAP. (Je ne l'ai pas encore complètement compris, donc je l'ai peut-être mal interprété.) Voir le blog de Koo ci-dessous pour savoir comment procéder. Interagir avec le modèle d'apprentissage automatique à l'aide de SHAP

import shap
#Créer un modèle avec XGBoost
xgb_model = xgb.XGBClassifier()
xgb_model.fit(X_train, y_train)

#Cela semble être un sort pour exécuter Javascript dans un ordinateur portable
shap.initjs()

#Transmettez les données que vous souhaitez interpréter comme modèle.
X_test=X_test.reset_index().drop('index',axis=1)#Initialiser l'index
explainer = shap.TreeExplainer(model=xgb_model)
shap_values = explainer.shap_values(X=X_test)

# summary_plot
shap.summary_plot(shap_values,X_test)

image.png Voir summary_plot. La couleur rouge indique que la valeur de la variable est élevée, la couleur bleue indique que la valeur de la variable est faible et l'axe horizontal est la valeur SHAP. Le contrat est le plus influent car les points représentent des échantillons individuels et les caractéristiques sont disposées à partir du haut par ordre décroissant d'influence sur la prédiction globale. Le contrat est égal à 0 = le paiement mensuel a un effet positif sur la prévision (c'est-à-dire la prédiction du sens de l'annulation), 1 = paiement annuel, 2 = le paiement de 2 ans a un effet négatif sur la prévision (c'est-à-dire le sens de ne pas annuler) Prédiction) est donnée. Le paiement mensuel a un effet positif sur la prévision (c'est-à-dire la prévision du sens de l'annulation), ce qui est probablement la raison pour laquelle elle s'écarte en premier lieu. Pensez-y.

Ensuite, visualisons un modèle de prédiction typique, quelle variable a tiré le client qui a atteint la prédiction et le client qui n'a pas atteint la prédiction pour obtenir la prédiction finale.

#Obtenez l'index qui était erroné et l'index qui a été touché
make_index=X_test.copy()
make_index['y_pred']=y_pred
make_index['y_true']=y_true
make_index['tf']=0
make_index.loc[aaa['y_true']!=aaa['y_pred'], 'tf']=1
miss=make_index[make_index['tf']==1].index.values
nonmiss=make_index[make_index['tf']==0].index.values

# decision_Visualisation de la personne qui a frappé avec l'intrigue
shap.decision_plot(explainer.expected_value\
                   , shap_values[nonmiss[:50]], X_test.iloc[nonmiss[:50],:]\
                   ,link="logit",ignore_warnings = True, feature_order='hclust')

# decision_Visualisation des personnes hors de la parcelle
shap.decision_plot(explainer.expected_value\
                   , shap_values[miss[:50]], X_test.iloc[miss[:50],:]\
                   ,link="logit",highlight=range(len(miss[:20])),ignore_warnings = True, feature_order='hclust')

Client gagnant image.png Client manquant image.png La couleur rouge indique que le contrat est annulé (= 1) et la couleur bleue indique que le contrat n'est pas annulé (= 0). En regardant cela, on peut voir que les résultats finaux des prévisions sont considérablement tirés par le contrat et le mandat, mais il est également possible que les clients qui sont en panne aient tendance à être attirés par les mauvaises prévisions par contrat et ancienneté. À partir de ces résultats, il peut être possible d'envisager une méthode de modification du contrat ou de la durée comme une quantité de caractéristiques.

c'est tout!

Recommended Posts

Quels sont les facteurs à l'origine de la mauvaise prédiction du ML? ~ La recherche factorielle est décidée par arbre ~
Qu'est-ce qu'un arbre de décision?
Quelle est la cause de l'erreur suivante?
Qu'est-ce qu'une décision rationnelle qui maximise les chances de rencontrer une «maison idéale»?