[PYTHON] Was sind die Faktoren für die falsche Vorhersage von ML? ~ Die Faktoruntersuchung wird vom Baum entschieden ~

Ich möchte die Genauigkeit des Vorhersagemodells verbessern

Aber was soll ich tun ... Was ist die Ursache für die falsche Vorhersage überhaupt? ?? Ja, können wir die Ursache der Fehlvorhersage mit einem Entscheidungsbaum visualisieren? Versuch. (Obwohl es einige theoretische Fehler geben kann) Ich werde den Analysefluss veröffentlichen, also schreibe ich über Datenverarbeitung und so weiter. Ich werde in den Fluss des Datenlesens, der Vorverarbeitung, der Datenbestätigung, der Modellkonstruktion / Genauigkeitsbestätigung und der Faktoruntersuchung schreiben. Vielleicht wird es länger dauern. Das Hauptthema ist dieses Kapitel → [Visualisierung unvorhersehbarer Kundenmerkmale im Entscheidungsbaum](# Visualisierung unvorhersehbarer Kundenmerkmale im Entscheidungsbaum)

Nutzungsdaten

Verwenden Sie Daten aus Kaggles Telco Customer Churn. https://www.kaggle.com/blastchar/telco-customer-churn Dies sind Daten über die Kunden der Telefongesellschaft und stellen ein binäres Klassifizierungsproblem dar, wobei die Zielvariable darin besteht, ob storniert werden soll oder nicht. Jede Zeile repräsentiert einen Kunden und jede Spalte enthält die Attribute des Kunden. Jede Spalte ist wie folgt. (Google Übersetzung der Kaggle-Spaltenbeschreibung)

Kunden-ID: Kunden-ID
Geschlecht: Ob der Kunde männlich oder weiblich ist
SeniorCitizen: Ob der Kunde älter ist (1, 0)
Partner: Ob der Kunde einen Partner hat (ja, nein)
Abhängige: Gibt an, ob der Kunde Angehörige hat (ja, nein)
Amtszeit: Die Anzahl der Monate, die der Kunde im Unternehmen verbracht hat
Telefonservice: Gibt an, ob Sie den Telefondienst verwenden (Ja, Nein).
MultipleLines: Gibt an, ob der Kunde mehrere Leitungen hat (Ja, Nein, kein Telefondienst).
InternetService: Internetdienstanbieter des Kunden (DSL, Glasfaser, Nr.)
OnlineSecurity: Gibt an, ob Sie über Online-Sicherheit verfügen (Ja, Nein, kein Internetdienst).
OnlineBackup: Gibt an, ob Sie ein Online-Backup haben (ja, nein, kein Internetdienst).
DeviceProtection: Gibt an, ob Sie Ihr Gerät schützen (ja, nein, kein Internetdienst).
TechSupport: Ob Sie technischen Support haben (ja, nein, kein Internetdienst)
StreamingTV: Ob Sie einen Streaming-Fernseher haben (ja, nein, kein Internetdienst)
StreamingMovies: Gibt an, ob der Kunde Streaming-Filme hat (ja, nein, kein Internetdienst).
Vertrag: Vertragslaufzeit des Kunden (monatlich, 1 Jahr, 2 Jahre)
Papierlose Abrechnung: Ob Sie eine papierlose Rechnung erstellen (ja, nein)
Zahlungsmethode: Zahlungsmethode des Kunden (elektronischer Scheck, Postscheck, Überweisung (automatisch), Kreditkarte (automatisch))
MonthlyCharges: Betrag, der den Kunden jeden Monat in Rechnung gestellt wird
TotalCharges: Gesamtbetrag, der dem Kunden in Rechnung gestellt wird

Abwanderung: Ob der Kunde storniert hat (ja oder nein)

Datenübersicht

Werfen wir einen kurzen Blick auf den Inhalt der Daten.

Grundinformation

#Paketimport
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

#Daten gelesen
df=pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv')
churn=df.copy()
print(churn.info())
display(churn.head())

image.png

Fehlende Zeile

Wechseln Sie zu fehlendem Wert

Die Spalte TotalCharges ist eine Zahl, aber kein Zahlentyp, da leere Zeilen vorhanden sind. Konvertieren Sie also den Rohling in Nan und konvertieren Sie ihn in einen numerischen Typ.

#Ändern Sie den Rohling mit halber Breite in Nan
churn.loc[churn['TotalCharges']==' ', 'TotalCharges']=np.nan
#Wechseln Sie zu float
churn['TotalCharges']=churn['TotalCharges'].astype(float)
print(churn.info())
display(churn.head())

image.png Konvertierung der Spalte "Gesamtkosten" in "Float" abgeschlossen.

Fehlende Zeilenvervollständigung

Als nächstes arbeiten Sie daran, Nan of Total Charges zu ergänzen. Derzeit scheint es, dass das Füllen von Nan mit der kategorialen Variablen mit der größten Abweichung der Gesamtgebühren in Abhängigkeit von der Kategorie für die Vorhersage effektiver ist als das einfache Füllen von Nan mit dem Durchschnittswert der Gesamtgebühren. Daher besteht die Richtlinie darin, Nan mit dem Durchschnittswert der Gesamtkosten für jede Kategorievariable zu füllen. Erstellen Sie zunächst einen Datenrahmen ohne Nan und dann einen Datenrahmen mit nur kategorialen Variablen und Gesamtgebühren.

#Erstellen eines Datenrahmens mit entfernten Nan-Linien
churn2=churn[churn.isnull().any(axis=1)==False].copy()
churn2['TotalCharges']=churn2['TotalCharges'].astype(float)

#Erstellen eines Datenrahmens aus Kategorievariable + Gesamtkosten
#Extrahieren Sie nur Daten, deren dtype der Objekttyp ist
churn2_TotalCharges=churn2.select_dtypes(include=['object']).drop('customerID',axis=1)
#Gesamtkosten hinzugefügt
churn2_TotalCharges['TotalCharges']=churn2['TotalCharges']
print(churn2_TotalCharges.info())

image.png Aufgrund des Ausschlusses von Nan-Linien änderte sich die Anzahl von 7.043 auf 7.032. Als nächstes visualisieren wir, welche Kategorievariablen wahrscheinlich mit den Gesamtkosten zusammenhängen.

#Definieren Sie eine Liste von Kategorienvariablennamen ohne Gesamtkosten
obj_columns=churn2_TotalCharges.columns.values
obj_columns=obj_columns[~(obj_columns == 'TotalCharges')]

#Zeichnen Sie für jede Kategorievariable ein Histogramm für die Gesamtkosten
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' → Es besteht das Gefühl, dass die Verteilung der Gesamtkosten für jede Kategorie in den Kategorievariablen in diesem Bereich sehr unterschiedlich ist. Von hier aus können Sie eine kategoriale Variable auswählen, die den Durchschnitt der Gesamtkosten berechnet. Wenn möglich, möchte ich jedoch eine quantitative Beurteilung vornehmen.

Dann werde ich versuchen, auch hier anhand des Entscheidungsbaums zu beurteilen, obwohl es sich vom Hauptthema unterscheidet. CART erstellt einen erklärenden Variablenzweig, der die Zielvariable basierend auf Gini-Unreinheit (oder Entropie) und Informationsgewinn am besten trennt. Eine theoretische Geschichte finden Sie unter ["Erste Mustererkennung"](https://www.amazon.co.jp/dp/4627849710 "" Erste Mustererkennung "") oder auf der folgenden Website. https://dev.classmethod.jp/articles/2017ad_20171211_dt-2/

Mit anderen Worten, wenn CART auf TotalCharges als Zielvariable angewendet wird, sollte die erklärende Variable, die TotalCharges am besten trennt, zum ersten Zweig kommen. Ich werde es tatsächlich versuchen.

#Kopieren Sie einen neuen Datenrahmen
churn2_TotalCharges_trans=churn2_TotalCharges.copy()
#Beschriften Sie kategoriale Variablen
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

#Entscheidungsbaum Modellbau
clf = tree.DecisionTreeRegressor(max_depth=1)
#Datenrahmen mit nur kategorialen Variablen als erklärende Variable, Gesamtkosten als objektive Variable
clffit = clf.fit(churn2_TotalCharges_trans.drop('TotalCharges',axis=1).values\
                 , churn2_TotalCharges_trans['TotalCharges'].values)

#Visualisierung des Entscheidungsbaums
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 Da 'DeviceProtection' die Variable ist, die TotalCharges am meisten teilt, füllen Sie Nan mit dem Durchschnittswert von TotalCharges für jede Kategorie dieser Variablen.

#Berechnen Sie den Durchschnitt der Gesamtgebühren für jeden Geräteschutz
churn2_TotalCharges_mean=churn2.groupby(['DeviceProtection'])[['TotalCharges']].mean().reset_index().rename(columns={'TotalCharges':'TotalCharges_mean'})
#Führen Sie die durchschnittlichen Gesamtgebühren pro Geräteschutz in einem Datenrahmen zusammen, der Nan enthält
churn=pd.merge(churn,churn2_TotalCharges_mean,on=['DeviceProtection'],how='left')
#Komplement Nan
churn.loc[(churn.isnull().any(axis=1)==True), 'TotalCharges']=churn['TotalCharges_mean']
#Die durchschnittliche Spalte "Gesamtkosten" wurde entfernt
churn=churn.drop('TotalCharges_mean',axis=1)
#Bestätigung der Nan-Linie
display(churn[churn.isnull().any(axis=1)==True])

Nan wurde ergänzt. image.png

Eine Vogelperspektive der Daten

Da die fehlenden Werte verschwunden sind und die Daten vervollständigt wurden, werden wir die kontinuierlichen Wertvariablen und kategorialen Variablen aus der Vogelperspektive betrachten.

# Churn:Kreisdiagramm des Stornierungsverhältnisses Ja oder Nein
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

#Erstellen Sie einen Datenrahmen, indem Sie nur kontinuierliche Variablen extrahieren
churn_select=churn.select_dtypes(include=['number']).copy()
#Extrahieren Sie nur kategoriale Variablen und erstellen Sie einen Datenrahmen (wird später verwendet).
churn_select_obj=churn.select_dtypes(include=['object']).copy()
churn_select_obj['SeniorCitizen']=churn_select['SeniorCitizen']
#Zielvariable hinzufügen
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'])
#Kontinuierliches variables Paardiagramm
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 der Kategorienvariablennamen
col=churn_select_obj.columns.values[1:]
col2=col[~(col == 'Churn')]
#Horizontale Achse Abwanderung, Balkendiagramm für die vertikale Achsenanzahl für jede Kategorie
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 Ich denke, dass es sich um ein wenig unausgeglichene Daten handelt, dass die Daten mit größerer Amtszeit und Gesamtkosten dazu neigen, nicht zu stornieren. Wenn der Vertrag jährlich bezahlt wird, gibt es nicht viele Stornierungen, und das Geschlecht scheint irrelevant zu sein.

Modellbau

Nachdem ich die Daten gesehen habe, erstellen wir ein Modell.

#LabelEncoded Data Frame Churn_codieren lassen
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,Funktion zum Erstellen von Testdaten
def createXy(df, col, target, test_size=0.3, random_state=0):
    #Trennung von erklärenden Variablen und objektiven Variablen
    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

Diesmal ist es mühsam, so dass ich weder den Funktionsumfang auswählen noch den hohen Para anpassen musste.

#Erklärender Variablenname
colx=churn_encode.columns.values[:-1]
#Name der objektiven Variablen
coly='Churn'
X_train, y_train, X_test, y_test = createXy(churn_encode, colx, coly, test_size=0.3, random_state=0)

#Erstellen Sie ein Modell mit 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)
#Überprüfen der Genauigkeit der Testdaten
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))

Ergebnis: 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

#Siehe auch Verwirrungsmatrix
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: Storniert, 0: Nicht storniert image.png

Die Bestätigung der Genauigkeit des Modells ist übrigens endgültig abgeschlossen. Schauen wir uns die Ursache der falschen Vorhersage von der nächsten an.

Visualisierung von Kundenmerkmalen, die im Entscheidungsbaum nicht vorhersehbar waren

Endlich das Hauptthema. Was sind die Merkmale des Kunden, der nicht vorhergesagt wurde? Das würde ich gerne im Entscheidungsbaum sehen. Wie oben erwähnt, erzeugt der CART-Algorithmus des Entscheidungsbaums einen Zweig der erklärenden Variablen, der die Zielvariable basierend auf der Gini-Unreinheit (oder Entropie) und dem Informationsgewinn am besten trennt. Wenn Sie daher einen Entscheidungsbaum mit den beiden Werten zeichnen, dass die Vorhersage als Zielvariable korrekt oder falsch ist, können Sie anscheinend einen Zweig der erklärenden Variablen generieren, der die Vorhersage am besten von der Richtigkeit oder Falschheit trennt. Erstellen Sie zunächst eine Spalte, die angibt, dass die Vorhersage richtig oder falsch war.

# X_Erstellen Sie eine Kopie des Tests und fügen Sie eine neue Variable hinzu
churn_test=X_test.copy()
churn_test['true']=y_true
churn_test['pred']=y_pred
churn_test['RightOrWrong']=0
# y_wahr und y_RightOrWrong, wenn pred übereinstimmt=Auf 1 setzen
churn_test.loc[(churn_test['true']==churn_test['pred']),'RightOrWrong']=1
display(churn_test.groupby(['RightOrWrong'])[['pred']].count())
display(churn_test)

434 von 2.113 unvorhersehbaren Kunden image.png Lassen Sie uns visualisieren, welche Tendenz diese 434 Personen mit einem Entscheidungsbaum haben.

#Baumtiefe 3 (Text)
clf = tree.DecisionTreeClassifier(max_depth=3)
# 'true','pred','RightOrWrong'Erklärende Variablen ohne'RightOrWrong'Die Zielvariable
clffit = clf.fit(churn_test.drop(['true','pred','RightOrWrong'],axis=1), churn_test['RightOrWrong'])

#Visualisiert mit 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 Anscheinend steht Contract ganz oben im Baum, daher scheint es die erklärende Variable zu sein, die am besten zwischen Treffer- und Fehlvorhersagen unterscheidet. Vertrag ist 0 = Menschen, die monatlich zahlen, neigen dazu, falsch zu liegen. Im Gegenteil, 1 = jährliche Zahlung und 2 = 2-jährige Zahlung scheinen in erheblichem Maße korrekt zu sein. Unter denen, die monatlich zahlen, sind diejenigen mit Internet Service 2 = Nein in der Regel korrekt, und diejenigen mit 0 = DSL und 1 = Glasfaser kommen nicht in Frage. Werfen wir einen Blick auf das zuvor gezeigte Diagramm. image.png Menschen, die monatlich für einen Vertrag bezahlen, haben viele Ja und Nein in Churn, und diejenigen, die den Internetdienst für Glasfasern nutzen, haben viele Ja und Nein in Churn. (Leute, die Internetdienste nutzen, neigen dazu, abzubrechen, und sie sind unzufrieden, weil die Leitung langsam ist, oder?) Da es für diese Personen schwierig ist, die Ja- und Nein-Trends von Churn zu erkennen, scheinen die Vorhersagen wahrscheinlich falsch zu sein, und als Ergebnis der tatsächlichen Visualisierung mit dem Entscheidungsbaum wurde festgestellt, dass die Vorhersagen tendenziell falsch sind. TotalCharges wird ebenfalls im Baum angezeigt, aber es scheint, dass sich die Tendenz zur Vorhersagegenauigkeit nicht ändert, wenn der Wert in gewissem Maße zunimmt. Fügen Sie basierend auf diesen Ergebnissen vorerst Variablen wie synthetische Variablen und Clipping hinzu, erstellen Sie erneut ein Modell und überprüfen Sie die Genauigkeit.

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)

#Histogramm der Gesamtkosten
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')

#Gesamtkosten abschneiden
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)#Histogramm der Gesamtkosten
ax3.set_title('X_train TotalCharges clipping')

#Gesamtkosten abschneiden
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)#Histogramm der Gesamtkosten
ax4.set_title('X_test TotalCharges clipping')
plt.show()

image.png

#Neuer Zug mit Variablen,Einen Test machen
new_X_train=X_train.copy()
new_X_test=X_test.copy()
#Ändern Sie die Gesamtkosten in "Beschneiden"
new_X_train['TotalCharges']=TotalCharges_train
new_X_test['TotalCharges']=TotalCharges_test
#Erstellen Sie eine zusammengesetzte Variable für Vertrag und Internetdienst
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)
#LabelEncoding der zusammengesetzten Variablen von Vertrag und Internetdienst
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'])
#Modell bauen
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)
#Überprüfung der Richtigkeit
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))

#Bestätigung der Verwirrungsmatrix
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))

Ergebnis: 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

Erstes Ergebnis (Repost): 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

Verwirrte Matrix 1: Storniert, 0: Nicht storniert Ergebnis: image.png Erstes Ergebnis (Repost): image.png Verbesserte Genauigkeit für Testdaten in allen Metriken.

Zusammenfassung

Datenlesen, Vorverarbeitung, Datenbestätigung, Modellkonstruktion / Genauigkeitsbestätigung und Faktoruntersuchung von Fehlvorhersagen werden beschrieben. Ist es nicht das Hauptthema dieses Artikels, die Faktoren untersuchen zu können, die die Vorhersage mit dem Entscheidungsbaum falsch machen? Ich habe die Idee geübt. Es tut mir leid, wenn die Theorie oder Interpretation falsch ist. Ich dachte, es könnte nützlich sein, wenn Kunden wie PoC über die aktuellen Probleme des Vorhersagemodells informiert werden. Während ich die Lernkurve zeigte, zeigte ich auch das Ergebnis des Entscheidungsbaums, z. B. "Ich habe möglicherweise nicht genügend Daten" oder "Ich denke, ich sollte so damit umgehen, weil die Vorhersage hier falsch ist." Es ist auch gut, mit Kunden zu diskutieren. In einer solchen Geschichte scheint es Situationen zu geben, in denen Kunden mit reichlich Domänenwissen helfen können, indem sie denken: "Übrigens können solche Daten vorhanden sein und sie können Variablen hinzugefügt werden?" Der Entscheidungsbaum ist leicht zu sehen! Es ist in Ordnung, den SHAP-Preis anzugeben, aber es kann für Kunden schwierig zu verstehen und zu erklären sein.

Bonus (SHAP)

Lassen Sie uns die Tendenz der Kunden überprüfen, wenn die Vorhersage selbst mit dem SHAP-Wert falsch ist. (Ich habe es noch nicht vollständig verstanden, daher habe ich es möglicherweise falsch interpretiert.) Informationen dazu finden Sie in Koos Blog unten. Interaktion mit dem maschinellen Lernmodell mithilfe von SHAP

import shap
#Erstellen Sie ein Modell mit XGBoost
xgb_model = xgb.XGBClassifier()
xgb_model.fit(X_train, y_train)

#Es scheint ein Zauber zu sein, Javascript in einem Notizbuch auszuführen
shap.initjs()

#Übergeben Sie die Daten, die Sie als Modell interpretieren möchten.
X_test=X_test.reset_index().drop('index',axis=1)#Index initialisieren
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 Siehe summary_plot. Die rote Farbe zeigt an, dass der Wert der Variablen hoch ist, die blaue Farbe zeigt an, dass der Wert der Variablen niedrig ist, und die horizontale Achse ist der SHAP-Wert. Der Vertrag ist am einflussreichsten, da die Punkte einzelne Stichproben darstellen und die Merkmale von oben in absteigender Reihenfolge des Einflusses auf die Gesamtvorhersage angeordnet sind. Vertrag ist 0 = monatliche Zahlung wirkt sich positiv auf die Prognose aus (dh die Prognose der Stornierungsrichtung), 1 = jährliche Zahlung, 2 = 2-Jahres-Zahlung wirkt sich negativ auf die Prognose aus (dh die Richtung der Nichtstornierung) Vorhersage) gegeben ist. Die monatliche Zahlung wirkt sich positiv auf die Prognose aus (dh auf die Prognose der Stornierungsrichtung), was wahrscheinlich der Grund dafür ist, dass sie überhaupt abweicht. Denk darüber nach.

Als nächstes stellen wir uns ein typisches Vorhersagemuster vor, bei dem die Variable den Kunden, der die Vorhersage getroffen hat, und den Kunden, der die Vorhersage nicht getroffen hat, gezogen hat, um die endgültige Vorhersage zu erhalten.

#Holen Sie sich den Index, der falsch war, und den Index, der getroffen wurde
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_Visualisierung der Person, die mit der Handlung getroffen hat
shap.decision_plot(explainer.expected_value\
                   , shap_values[nonmiss[:50]], X_test.iloc[nonmiss[:50],:]\
                   ,link="logit",ignore_warnings = True, feature_order='hclust')

# decision_Visualisierung von Menschen, die nicht in der Handlung sind
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')

Gewinnender Kunde image.png Vermisster Kunde image.png Die rote Farbe zeigt an, dass der Vertrag gekündigt wurde (= 1), und die blaue Farbe zeigt an, dass der Vertrag nicht gekündigt wurde (= 0). Wenn man dies betrachtet, kann man sehen, dass die endgültigen Prognoseergebnisse durch Vertrag und Amtszeit erheblich beeinflusst werden, aber es ist auch möglich, dass Kunden, die nicht in Betrieb sind, durch die falsche Prognose durch Vertrag und Amtszeit gezogen werden. Aus diesen Ergebnissen kann möglicherweise eine Methode zum Ändern des Vertrags oder der Amtszeit als Merkmalsmenge in Betracht gezogen werden.

das ist alles!

Recommended Posts

Was sind die Faktoren für die falsche Vorhersage von ML? ~ Die Faktoruntersuchung wird vom Baum entschieden ~
Was ist ein Entscheidungsbaum?
Was ist die Ursache für den folgenden Fehler?
Was ist eine rationale Entscheidung, die die Chancen maximiert, ein "ideales Zuhause" zu finden?