Kaggle Home Credit Default Risk-Wettbewerb - Datenanalyse und einfache Vorhersagemodelle

Auf dem Datenfestival 2 in Minsk bemerkte Vladimir Iglovikov, Bildverarbeitungsingenieur bei Lyft, dass der beste Weg, Data Science zu lernen, darin besteht, an Wettbewerben teilzunehmen, Lösungen anderer zu entwickeln, diese zu kombinieren, Ergebnisse zu erzielen und Ihre Arbeit zu zeigen. Im Rahmen dieses Paradigmas habe ich mich entschlossen, den Wettbewerb zur Bewertung des Kreditrisikos fĂŒr Privatkredite genauer zu betrachten und (AnfĂ€ngern, Wissenschaftlern und vor allem mir selbst) zu erklĂ€ren, wie solche DatensĂ€tze richtig analysiert und Modelle fĂŒr sie erstellt werden können.



(Bild von hier )

Die Home Credit Group ist eine Gruppe von Banken und Nichtbanken-Kreditorganisationen, die in 11 LĂ€ndern tĂ€tig sind (einschließlich Russland als Home Credit and Finance Bank LLC). Ziel des Wettbewerbs ist es, eine Methode zur Bewertung der KreditwĂŒrdigkeit von Kreditnehmern zu erstellen, die keine BonitĂ€tshistorie haben. Was ziemlich edel aussieht - Kreditnehmer dieser Kategorie können oft keinen Kredit von der Bank erhalten und sind gezwungen, sich an BetrĂŒger und Mikrokredite zu wenden. Es ist interessant, dass der Kunde keine Anforderungen an die Transparenz und Interpretierbarkeit des Modells stellt (wie dies normalerweise bei Banken der Fall ist). Sie können alles verwenden, auch ein neuronales Netzwerk.

Die Trainingsstichprobe besteht aus mehr als 300.000 DatensĂ€tzen, es gibt ziemlich viele Anzeichen - 122, darunter viele kategoriale (nicht numerische). Schilder beschreiben den Kreditnehmer ausreichend detailliert bis hin zu dem Material, aus dem die WĂ€nde seines Hauses bestehen. Ein Teil der Daten ist in 6 zusĂ€tzlichen Tabellen enthalten (Daten zum KreditbĂŒro, zum Kreditkartenguthaben und zu frĂŒheren Darlehen). Diese Daten mĂŒssen auch irgendwie verarbeitet und in die Hauptdaten geladen werden.

Der Wettbewerb sieht aus wie eine Standardklassifizierungsaufgabe (1 im Feld ZIEL bedeutet Schwierigkeiten bei der Zahlung, 0 bedeutet keine Schwierigkeiten). Es sollte jedoch nicht 0/1 vorhergesagt werden, sondern die Wahrscheinlichkeit von Problemen (die im Übrigen leicht mit den Wahrscheinlichkeitsvorhersagemethoden predict_proba aller komplexen Modelle gelöst werden können).

Auf den ersten Blick ist der Datensatz ein Standard fĂŒr maschinelles Lernen. Die Organisatoren haben einen hohen Preis von 70.000 US-Dollar angeboten. Infolgedessen nehmen heute mehr als 2.600 Teams am Wettbewerb teil, und der Kampf findet in Tausendstel Prozent statt. Andererseits bedeutet eine solche PopularitĂ€t, dass der Datensatz auf und ab untersucht wurde und viele Kernel mit guter EDA (Exploratory Data Analisys - Forschung und Analyse von Daten im Netzwerk, einschließlich grafischer Daten), Feature Engineering (Arbeiten mit Attributen) erstellt wurden. und mit interessanten Modellen. (Der Kernel ist ein Beispiel fĂŒr die Arbeit mit einem Datensatz, den jeder anlegen kann, um anderen KĂ€mpfern seine Arbeit zu zeigen.)

Kernel verdienen Aufmerksamkeit:


Um mit Daten zu arbeiten, wird normalerweise der folgende Plan empfohlen, dem wir folgen werden.

  1. Das Problem verstehen und sich mit den Daten vertraut machen
  2. Datenbereinigung und Formatierung
  3. EDA
  4. Basismodell
  5. Modellverbesserung
  6. Modellinterpretation

In diesem Fall mĂŒssen Sie berĂŒcksichtigen, dass die Daten sehr umfangreich sind und nicht sofort ĂŒberlastet werden können. Es ist sinnvoll, schrittweise zu handeln.

Beginnen wir mit dem Importieren der Bibliotheken, die wir fĂŒr die Analyse benötigen, um mit Daten in Form von Tabellen zu arbeiten, Diagramme zu erstellen und mit Matrizen zu arbeiten.

import pandas as pd import matplotlib.pyplot as plt import numpy as np import seaborn as sns %matplotlib inline 

Laden Sie die Daten herunter. Mal sehen, was wir alle haben. Dieser Speicherort im Verzeichnis "../input/" ist ĂŒbrigens mit der Anforderung verbunden, Ihre Kernel auf Kaggle zu platzieren.

 import os PATH="../input/" print(os.listdir(PATH)) 

['application_test.csv', 'application_train.csv', 'bureau.csv', 'bureau_balance.csv', 'credit_card_balance.csv', 'HomeCredit_columns_description.csv', 'installments_payments.csv', 'POS_CASH_balance.csv', 'previous_application.csv']

Es gibt 8 Tabellen mit Daten (ohne die Tabelle HomeCredit_columns_description.csv, die eine Beschreibung der Felder enthÀlt), die wie folgt miteinander verbunden sind:



application_train / application_test: Stammdaten, der Kreditnehmer wird durch das Feld SK_ID_CURR identifiziert
BĂŒro: Daten zu frĂŒheren Darlehen anderer Kreditinstitute von einem KreditbĂŒro
office_balance: Monatliche Daten zu frĂŒheren BĂŒrodarlehen. Jede Zeile ist der Monat, in dem das Darlehen verwendet wird
vorherige_Anwendung: FrĂŒhere AntrĂ€ge auf Darlehen in Home Credit haben jeweils ein eindeutiges Feld SK_ID_PREV
POS_CASH_BALANCE: Monatliche Daten zu Krediten in Home Credit mit der Ausgabe von Bargeld und Krediten fĂŒr den Kauf von Waren
credit_card_balance: Monatliche Kreditkartenguthaben in Home Credit
Ratenzahlung_Zahlung: Zahlungsverlauf frĂŒherer Kredite bei Home Credit.

Konzentrieren wir uns zunÀchst auf die Hauptdatenquelle und sehen, welche Informationen daraus extrahiert werden können und welche Modelle erstellt werden sollen. Laden Sie die Basisdaten herunter.

  • app_train = pd.read_csv (PATH + 'application_train.csv',)
  • app_test = pd.read_csv (PATH + 'application_test.csv',)
  • print ("Trainingssatzformat:", app_train.shape)
  • print ("Testbeispielformat:", app_test.shape)
  • Trainingsbeispielformat: (307511, 122)
  • Testmusterformat: (48744, 121)

Insgesamt haben wir 307.000 DatensĂ€tze und 122 Zeichen in der Trainingsstichprobe und 49.000 DatensĂ€tze und 121 Zeichen im Test. Die Diskrepanz ist offensichtlich auf die Tatsache zurĂŒckzufĂŒhren, dass die Testprobe kein Zielattribut TARGET enthĂ€lt, und wir werden es vorhersagen.

Schauen wir uns die Daten genauer an

 pd.set_option('display.max_columns', None) #  pandas     app_train.head() 



(erste 8 Spalten gezeigt)

Es ist ziemlich schwierig, Daten in diesem Format anzusehen. Schauen wir uns die Liste der Spalten an:

app_train.info(max_cols=122)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 307511 entries, 0 to 307510
Data columns (total 122 columns):
SK_ID_CURR 307511 non-null int64
TARGET 307511 non-null int64
NAME_CONTRACT_TYPE 307511 non-null object
CODE_GENDER 307511 non-null object
FLAG_OWN_CAR 307511 non-null object
FLAG_OWN_REALTY 307511 non-null object
CNT_CHILDREN 307511 non-null int64
AMT_INCOME_TOTAL 307511 non-null float64
AMT_CREDIT 307511 non-null float64
AMT_ANNUITY 307499 non-null float64
AMT_GOODS_PRICE 307233 non-null float64
NAME_TYPE_SUITE 306219 non-null object
NAME_INCOME_TYPE 307511 non-null object
NAME_EDUCATION_TYPE 307511 non-null object
NAME_FAMILY_STATUS 307511 non-null object
NAME_HOUSING_TYPE 307511 non-null object
REGION_POPULATION_RELATIVE 307511 non-null float64
DAYS_BIRTH 307511 non-null int64
DAYS_EMPLOYED 307511 non-null int64
DAYS_REGISTRATION 307511 non-null float64
DAYS_ID_PUBLISH 307511 non-null int64
OWN_CAR_AGE 104582 non-null float64
FLAG_MOBIL 307511 non-null int64
FLAG_EMP_PHONE 307511 non-null int64
FLAG_WORK_PHONE 307511 non-null int64
FLAG_CONT_MOBILE 307511 non-null int64
FLAG_PHONE 307511 non-null int64
FLAG_EMAIL 307511 non-null int64
OCCUPATION_TYPE 211120 non-null object
CNT_FAM_MEMBERS 307509 non-null float64
REGION_RATING_CLIENT 307511 non-null int64
REGION_RATING_CLIENT_W_CITY 307511 non-null int64
WEEKDAY_APPR_PROCESS_START 307511 non-null object
HOUR_APPR_PROCESS_START 307511 non-null int64
REG_REGION_NOT_LIVE_REGION 307511 non-null int64
REG_REGION_NOT_WORK_REGION 307511 non-null int64
LIVE_REGION_NOT_WORK_REGION 307511 non-null int64
REG_CITY_NOT_LIVE_CITY 307511 non-null int64
REG_CITY_NOT_WORK_CITY 307511 non-null int64
LIVE_CITY_NOT_WORK_CITY 307511 non-null int64
ORGANIZATION_TYPE 307511 non-null object
EXT_SOURCE_1 134133 non-null float64
EXT_SOURCE_2 306851 non-null float64
EXT_SOURCE_3 246546 non-null float64
APARTMENTS_AVG 151450 non-null float64
BASEMENTAREA_AVG 127568 non-null float64
YEARS_BEGINEXPLUATATION_AVG 157504 non-null float64
YEARS_BUILD_AVG 103023 non-null float64
COMMONAREA_AVG 92646 non-null float64
ELEVATORS_AVG 143620 non-null float64
ENTRANCES_AVG 152683 non-null float64
FLOORSMAX_AVG 154491 non-null float64
FLOORSMIN_AVG 98869 non-null float64
LANDAREA_AVG 124921 non-null float64
LIVINGAPARTMENTS_AVG 97312 non-null float64
LIVINGAREA_AVG 153161 non-null float64
NONLIVINGAPARTMENTS_AVG 93997 non-null float64
NONLIVINGAREA_AVG 137829 non-null float64
APARTMENTS_MODE 151450 non-null float64
BASEMENTAREA_MODE 127568 non-null float64
YEARS_BEGINEXPLUATATION_MODE 157504 non-null float64
YEARS_BUILD_MODE 103023 non-null float64
COMMONAREA_MODE 92646 non-null float64
ELEVATORS_MODE 143620 non-null float64
ENTRANCES_MODE 152683 non-null float64
FLOORSMAX_MODE 154491 non-null float64
FLOORSMIN_MODE 98869 non-null float64
LANDAREA_MODE 124921 non-null float64
LIVINGAPARTMENTS_MODE 97312 non-null float64
LIVINGAREA_MODE 153161 non-null float64
NONLIVINGAPARTMENTS_MODE 93997 non-null float64
NONLIVINGAREA_MODE 137829 non-null float64
APARTMENTS_MEDI 151450 non-null float64
BASEMENTAREA_MEDI 127568 non-null float64
YEARS_BEGINEXPLUATATION_MEDI 157504 non-null float64
YEARS_BUILD_MEDI 103023 non-null float64
COMMONAREA_MEDI 92646 non-null float64
ELEVATORS_MEDI 143620 non-null float64
ENTRANCES_MEDI 152683 non-null float64
FLOORSMAX_MEDI 154491 non-null float64
FLOORSMIN_MEDI 98869 non-null float64
LANDAREA_MEDI 124921 non-null float64
LIVINGAPARTMENTS_MEDI 97312 non-null float64
LIVINGAREA_MEDI 153161 non-null float64
NONLIVINGAPARTMENTS_MEDI 93997 non-null float64
NONLIVINGAREA_MEDI 137829 non-null float64
FONDKAPREMONT_MODE 97216 non-null object
HOUSETYPE_MODE 153214 non-null object
TOTALAREA_MODE 159080 non-null float64
WALLSMATERIAL_MODE 151170 non-null object
EMERGENCYSTATE_MODE 161756 non-null object
OBS_30_CNT_SOCIAL_CIRCLE 306490 non-null float64
DEF_30_CNT_SOCIAL_CIRCLE 306490 non-null float64
OBS_60_CNT_SOCIAL_CIRCLE 306490 non-null float64
DEF_60_CNT_SOCIAL_CIRCLE 306490 non-null float64
DAYS_LAST_PHONE_CHANGE 307510 non-null float64
FLAG_DOCUMENT_2 307511 non-null int64
FLAG_DOCUMENT_3 307511 non-null int64
FLAG_DOCUMENT_4 307511 non-null int64
FLAG_DOCUMENT_5 307511 non-null int64
FLAG_DOCUMENT_6 307511 non-null int64
FLAG_DOCUMENT_7 307511 non-null int64
FLAG_DOCUMENT_8 307511 non-null int64
FLAG_DOCUMENT_9 307511 non-null int64
FLAG_DOCUMENT_10 307511 non-null int64
FLAG_DOCUMENT_11 307511 non-null int64
FLAG_DOCUMENT_12 307511 non-null int64
FLAG_DOCUMENT_13 307511 non-null int64
FLAG_DOCUMENT_14 307511 non-null int64
FLAG_DOCUMENT_15 307511 non-null int64
FLAG_DOCUMENT_16 307511 non-null int64
FLAG_DOCUMENT_17 307511 non-null int64
FLAG_DOCUMENT_18 307511 non-null int64
FLAG_DOCUMENT_19 307511 non-null int64
FLAG_DOCUMENT_20 307511 non-null int64
FLAG_DOCUMENT_21 307511 non-null int64
AMT_REQ_CREDIT_BUREAU_HOUR 265992 non-null float64
AMT_REQ_CREDIT_BUREAU_DAY 265992 non-null float64
AMT_REQ_CREDIT_BUREAU_WEEK 265992 non-null float64
AMT_REQ_CREDIT_BUREAU_MON 265992 non-null float64
AMT_REQ_CREDIT_BUREAU_QRT 265992 non-null float64
AMT_REQ_CREDIT_BUREAU_YEAR 265992 non-null float64
dtypes: float64(65), int64(41), object(16)
memory usage: 286.2+ MB


Rufen Sie detaillierte Anmerkungen nach Feld in der Datei HomeCredit_columns_description auf. Wie Sie aus den Informationen ersehen können, ist ein Teil der Daten unvollstĂ€ndig und ein Teil kategorisch. Sie werden als Objekt angezeigt. Die meisten Modelle arbeiten nicht mit solchen Daten, wir mĂŒssen etwas damit anfangen. In diesem Zusammenhang kann die erste Analyse als abgeschlossen betrachtet werden, wir werden direkt zu EDA gehen

Explorative Datenanalyse oder primÀres Data Mining


Im EDA-Prozess zĂ€hlen wir die grundlegenden Statistiken und zeichnen Diagramme, um Trends, Anomalien, Muster und Beziehungen innerhalb der Daten zu finden. Ziel von EDA ist es herauszufinden, was die Daten aussagen können. In der Regel geht die Analyse von oben nach unten - von einem allgemeinen Überblick bis zur Untersuchung einzelner Zonen, die Aufmerksamkeit erregen und von Interesse sein können. Anschließend können diese Erkenntnisse bei der Konstruktion des Modells, der Auswahl der Merkmale fĂŒr das Modell und seiner Interpretation verwendet werden.

Verteilung der Zielvariablen


 app_train.TARGET.value_counts() 

0 282686
1 24825
Name: TARGET, dtype: int64


 plt.style.use('fivethirtyeight') plt.rcParams["figure.figsize"] = [8,5]​ plt.hist(app_train.TARGET) plt.show() 



Ich möchte Sie daran erinnern, dass 1 Probleme jeglicher Art mit einer RĂŒckgabe bedeutet, 0 bedeutet keine Probleme. Wie Sie sehen, haben hauptsĂ€chlich Kreditnehmer keine Probleme mit der RĂŒckzahlung, der Anteil der Problematik liegt bei etwa 8%. Dies bedeutet, dass die Klassen nicht ausgeglichen sind und dies möglicherweise beim Erstellen des Modells berĂŒcksichtigt werden muss.

Fehlende Datenrecherche


Wir haben gesehen, dass der Mangel an Daten ziemlich groß ist. Mal sehen, wo und was fehlt.

 #      def missing_values_table(df): #   mis_val = df.isnull().sum() #    mis_val_percent = 100 * df.isnull().sum() / len(df) #    mis_val_table = pd.concat([mis_val, mis_val_percent], axis=1) #   mis_val_table_ren_columns = mis_val_table.rename( columns = {0 : 'Missing Values', 1 : '% of Total Values'}) #    mis_val_table_ren_columns = mis_val_table_ren_columns[ mis_val_table_ren_columns.iloc[:,1] != 0].sort_values( '% of Total Values', ascending=False).round(1) #  print ("   " + str(df.shape[1]) + " .\n" " " + str(mis_val_table_ren_columns.shape[0]) + "    .") #     return mis_val_table_ren_columns missing_values = missing_values_table(app_train) missing_values.head(10) 


122 .
67 .



Im Grafikformat:

 plt.style.use('seaborn-talk')​ fig = plt.figure(figsize=(18,6)) miss_train = pd.DataFrame((app_train.isnull().sum())*100/app_train.shape[0]).reset_index() miss_test = pd.DataFrame((app_test.isnull().sum())*100/app_test.shape[0]).reset_index() miss_train["type"] = "" miss_test["type"] = "" missing = pd.concat([miss_train,miss_test],axis=0) ax = sns.pointplot("index",0,data=missing,hue="type") plt.xticks(rotation =90,fontsize =7) plt.title("    ") plt.ylabel("  %") plt.xlabel("") 




Es gibt viele Antworten auf die Frage, was mit all dem zu tun ist. Sie können es mit Nullen fĂŒllen, Sie können Medianwerte verwenden, Sie können nur Zeilen ohne die erforderlichen Informationen löschen. Es hĂ€ngt alles von dem Modell ab, das wir verwenden möchten, da einige von ihnen perfekt mit fehlenden Werten umgehen. WĂ€hrend wir uns an diese Tatsache erinnern und alles so lassen, wie es ist.

Spaltentypen und kategoriale Codierung


Wie wir uns erinnern. Ein Teil der Spalten ist vom Typ Objekt, dh er hat keinen numerischen Wert, spiegelt jedoch eine Kategorie wider. Schauen wir uns diese Spalten genauer an.

 app_train.dtypes.value_counts() 

float64 65
int64 41
object 16
dtype: int64


 app_train.select_dtypes(include=[object]).apply(pd.Series.nunique, axis = 0) 

NAME_CONTRACT_TYPE 2
CODE_GENDER 3
FLAG_OWN_CAR 2
FLAG_OWN_REALTY 2
NAME_TYPE_SUITE 7
NAME_INCOME_TYPE 8
NAME_EDUCATION_TYPE 5
NAME_FAMILY_STATUS 6
NAME_HOUSING_TYPE 6
OCCUPATION_TYPE 18
WEEKDAY_APPR_PROCESS_START 7
ORGANIZATION_TYPE 58
FONDKAPREMONT_MODE 4
HOUSETYPE_MODE 3
WALLSMATERIAL_MODE 7
EMERGENCYSTATE_MODE 2
dtype: int64


Wir haben 16 Spalten mit jeweils 2 bis 58 verschiedenen Wertoptionen. Im Allgemeinen können Modelle fĂŒr maschinelles Lernen mit solchen Spalten nichts anfangen (mit Ausnahme einiger, wie z. B. LightGBM oder CatBoost). Da wir verschiedene Modelle im Datensatz ausprobieren möchten, muss hiermit etwas unternommen werden. GrundsĂ€tzlich gibt es zwei AnsĂ€tze:

  • Beschriftungscodierung - Kategorien werden mit den Ziffern 0, 1, 2 usw. versehen und in dieselbe Spalte geschrieben
  • One-Hot-Codierung - Eine Spalte wird entsprechend der Anzahl der Optionen in mehrere zerlegt. Diese Spalten geben an, welche Option dieser Datensatz hat.

Unter den populĂ€ren ist es erwĂ€hnenswert, mittlere Zielcodierung (danke fĂŒr die Klarstellung roryorangepants ).

Bei der Etikettencodierung gibt es ein kleines Problem: Sie weist numerische Werte zu, die nichts mit der RealitÀt zu tun haben. Wenn es sich beispielsweise um einen numerischen Wert handelt, ist das Einkommen des Kreditnehmers von 100.000 definitiv höher und besser als das Einkommen von 20.000. Man kann jedoch sagen, dass beispielsweise eine Stadt besser ist als eine andere, weil einer der Wert 100 und die andere 200 zugewiesen wird ?

One-Hot-Codierung ist dagegen sicherer, kann jedoch "zusĂ€tzliche" Spalten erzeugen. Wenn wir beispielsweise dasselbe Geschlecht mit One-Hot codieren, erhalten wir zwei Spalten, "mĂ€nnliches Geschlecht" und "weibliches Geschlecht", obwohl eine ausreichen wĂŒrde, "ist es mĂ€nnlich".

FĂŒr einen guten Datensatz wĂ€re es notwendig, Zeichen mit geringer VariabilitĂ€t mithilfe der Etikettencodierung und allem anderen zu codieren - One-Hot, aber der Einfachheit halber codieren wir alles gemĂ€ĂŸ One-Hot. Dies hat praktisch keinen Einfluss auf die Berechnungsgeschwindigkeit und das Ergebnis. Der Pandas-Kodierungsprozess selbst ist sehr einfach.

 app_train = pd.get_dummies(app_train) app_test = pd.get_dummies(app_test)​ print('Training Features shape: ', app_train.shape) print('Testing Features shape: ', app_test.shape) 

Training Features shape: (307511, 246)
Testing Features shape: (48744, 242)


Da die Anzahl der Optionen in den Auswahlspalten nicht gleich ist, stimmt die Anzahl der Spalten jetzt nicht ĂŒberein. Ausrichtung ist erforderlich - Sie mĂŒssen Spalten aus dem Trainingssatz entfernen, die nicht im Testsatz enthalten sind. Damit ist die Ausrichtungsmethode erforderlich. Sie mĂŒssen Achse = 1 angeben (fĂŒr Spalten).

 # ,           . train_labels = app_train['TARGET']​ #  -   .     app_train, app_test = app_train.align(app_test, join = 'inner', axis = 1)​ print('  : ', app_train.shape) print('  : ', app_test.shape)​ # Add target back in to the data app_train['TARGET'] = train_labels 

: (307511, 242)
: (48744, 242)


Datenkorrelation


Ein guter Weg, um die Daten zu verstehen, besteht darin, die Pearson-Korrelationskoeffizienten fĂŒr die Daten relativ zum Zielattribut zu berechnen. Dies ist nicht die beste Methode, um die Relevanz von Features zu zeigen, aber sie ist einfach und ermöglicht es Ihnen, sich ein Bild von den Daten zu machen. Die Koeffizienten können wie folgt interpretiert werden:

  • 00-.19 "sehr schwach"
  • 20-.39 "schwach"
  • 40-.59 "Durchschnitt"
  • 60-79 stark
  • 80-1.0 "sehr stark"


 #    correlations = app_train.corr()['TARGET'].sort_values()​ #  print('  : \n', correlations.tail(15)) print('\n  : \n', correlations.head(15)) 

:
DAYS_REGISTRATION 0.041975
OCCUPATION_TYPE_Laborers 0.043019
FLAG_DOCUMENT_3 0.044346
REG_CITY_NOT_LIVE_CITY 0.044395
FLAG_EMP_PHONE 0.045982
NAME_EDUCATION_TYPE_Secondary / secondary special 0.049824
REG_CITY_NOT_WORK_CITY 0.050994
DAYS_ID_PUBLISH 0.051457
CODE_GENDER_M 0.054713
DAYS_LAST_PHONE_CHANGE 0.055218
NAME_INCOME_TYPE_Working 0.057481
REGION_RATING_CLIENT 0.058899
REGION_RATING_CLIENT_W_CITY 0.060893
DAYS_BIRTH 0.078239
TARGET 1.000000
Name: TARGET, dtype: float64

:
EXT_SOURCE_3 -0.178919
EXT_SOURCE_2 -0.160472
EXT_SOURCE_1 -0.155317
NAME_EDUCATION_TYPE_Higher education -0.056593
CODE_GENDER_F -0.054704
NAME_INCOME_TYPE_Pensioner -0.046209
ORGANIZATION_TYPE_XNA -0.045987
DAYS_EMPLOYED -0.044932
FLOORSMAX_AVG -0.044003
FLOORSMAX_MEDI -0.043768
FLOORSMAX_MODE -0.043226
EMERGENCYSTATE_MODE_No -0.042201
HOUSETYPE_MODE_block of flats -0.040594
AMT_GOODS_PRICE -0.039645
REGION_POPULATION_RELATIVE -0.037227
Name: TARGET, dtype: float64


Somit korrelieren alle Daten schwach mit dem Ziel (mit Ausnahme des Ziels selbst, das natĂŒrlich gleich sich selbst ist). Das Alter und einige „externe Datenquellen“ unterscheiden sich jedoch von den Daten. Dies sind wahrscheinlich einige zusĂ€tzliche Daten von anderen Kreditorganisationen. Es ist lustig, dass, obwohl das Ziel bei einer Kreditentscheidung als UnabhĂ€ngigkeit von solchen Daten erklĂ€rt wird, wir uns in erster Linie auf diese stĂŒtzen werden.

Alter


Es ist klar, dass je Ă€lter der Kunde ist, desto höher ist die Wahrscheinlichkeit einer RĂŒckkehr (natĂŒrlich bis zu einem bestimmten Limit). Aus irgendeinem Grund wird das Alter jedoch in negativen Tagen vor der Ausgabe eines Kredits angegeben, weshalb es positiv mit der NichtrĂŒckzahlung korreliert (was etwas seltsam aussieht). Wir bringen es auf einen positiven Wert und betrachten die Korrelation.

 app_train['DAYS_BIRTH'] = abs(app_train['DAYS_BIRTH']) app_train['DAYS_BIRTH'].corr(app_train['TARGET']) 

-0.078239308309827088

Schauen wir uns die Variable genauer an. Beginnen wir mit dem Histogramm.

 #     ,  25  plt.hist(app_train['DAYS_BIRTH'] / 365, edgecolor = 'k', bins = 25) plt.title('Age of Client'); plt.xlabel('Age (years)'); plt.ylabel('Count'); 



Das Verteilungshistogramm selbst kann ein wenig nĂŒtzlich sein, außer dass wir keine speziellen Ausreißer sehen und alles mehr oder weniger glaubwĂŒrdig aussieht. Um die Auswirkung des Einflusses des Alters auf das Ergebnis zu zeigen, können wir ein Diagramm der KerndichteschĂ€tzung (KDE) erstellen - die Verteilung der Kerndichte, die in den Farben des Zielattributs dargestellt ist. Es zeigt die Verteilung einer Variablen und kann als geglĂ€ttetes Histogramm interpretiert werden (berechnet als Gaußscher Kern fĂŒr jeden Punkt, der dann gemittelt wird, um ihn zu glĂ€tten).

 # KDE ,   sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, 'DAYS_BIRTH'] / 365, label = 'target == 0')​ # KDE   sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, 'DAYS_BIRTH'] / 365, label = 'target == 1')​ #  plt.xlabel('Age (years)'); plt.ylabel('Density'); plt.title('Distribution of Ages'); 



Wie zu sehen ist, ist der Anteil der AusfĂ€lle bei jungen Menschen höher und nimmt mit zunehmendem Alter ab. Dies ist kein Grund, jungen Menschen immer Kredite zu verweigern. Eine solche „Empfehlung“ fĂŒhrt nur zu Einkommens- und Marktverlusten fĂŒr die Bank. Dies ist eine Gelegenheit, ĂŒber eine grĂŒndlichere Überwachung solcher Kredite, eine Bewertung und möglicherweise sogar eine Art finanzielle Bildung fĂŒr junge Kreditnehmer nachzudenken.

Externe Quellen


Schauen wir uns die „externen Datenquellen“ EXT_SOURCE und ihre Korrelation genauer an.

 ext_data = app_train[['TARGET', 'EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']] ext_data_corrs = ext_data.corr() ext_data_corrs 



Es ist auch bequem, die Korrelation mithilfe der Heatmap anzuzeigen

 sns.heatmap(ext_data_corrs, cmap = plt.cm.RdYlBu_r, vmin = -0.25, annot = True, vmax = 0.6) plt.title('Correlation Heatmap'); 



Wie Sie sehen können, zeigen alle Quellen eine negative Korrelation mit dem Ziel. Schauen wir uns die Verteilung von KDE fĂŒr jede Quelle an.

 plt.figure(figsize = (10, 12))​ #    for i, source in enumerate(['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3']): #  plt.subplot(3, 1, i + 1) #    sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, source], label = 'target == 0') #    sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, source], label = 'target == 1') #  plt.title('Distribution of %s by Target Value' % source) plt.xlabel('%s' % source); plt.ylabel('Density'); plt.tight_layout(h_pad = 2.5) 



Das Bild Ă€hnelt der Verteilung nach Alter - mit einem Anstieg des Indikators steigt die Wahrscheinlichkeit einer Kreditrendite. Die dritte Quelle ist in dieser Hinsicht die mĂ€chtigste. Obwohl die Korrelation mit der Zielvariablen in absoluten Zahlen immer noch in der Kategorie „sehr niedrig“ liegt, werden externe Datenquellen und das Alter fĂŒr die Erstellung des Modells von höchster Bedeutung sein.

Paar Zeitplan


Um die Beziehung dieser Variablen besser zu verstehen, können Sie ein Paardiagramm erstellen. Darin sehen Sie die Beziehung jedes Paares und ein Histogramm der Verteilung entlang der Diagonale. Oberhalb der Diagonale können Sie das Streudiagramm und darunter - 2d KDE anzeigen.

 #       age_data = app_train[['TARGET', 'DAYS_BIRTH']] age_data['YEARS_BIRTH'] = age_data['DAYS_BIRTH'] / 365​ #     plot_data = ext_data.drop(labels = ['DAYS_BIRTH'], axis=1).copy()​ #   plot_data['YEARS_BIRTH'] = age_data['YEARS_BIRTH']​ #         100 .  plot_data = plot_data.dropna().loc[:100000, :]​ #     def corr_func(x, y, **kwargs): r = np.corrcoef(x, y)[0][1] ax = plt.gca() ax.annotate("r = {:.2f}".format(r), xy=(.2, .8), xycoords=ax.transAxes, size = 20)​ #   pairgrid object grid = sns.PairGrid(data = plot_data, size = 3, diag_sharey=False, hue = 'TARGET', vars = [x for x in list(plot_data.columns) if x != 'TARGET'])​ #  -  grid.map_upper(plt.scatter, alpha = 0.2)​ #  -  grid.map_diag(sns.kdeplot)​ #  -   grid.map_lower(sns.kdeplot, cmap = plt.cm.OrRd_r);​ plt.suptitle('Ext Source and Age Features Pairs Plot', size = 32, y = 1.05); 



RĂŒckzahlbare Kredite werden in blau angezeigt, nicht rĂŒckzahlbar in rot. All dies zu interpretieren ist ziemlich schwierig, aber ein guter Druck auf einem T-Shirt oder einem Bild in einem Museum fĂŒr moderne Kunst kann aus diesem Bild hervorgehen.

Untersuchung anderer Anzeichen


Lassen Sie uns andere Merkmale und ihre AbhÀngigkeit von der Zielvariablen genauer betrachten. Da es viele kategoriale gibt (und wir haben es bereits geschafft, sie zu kodieren), benötigen wir wieder die Anfangsdaten. Nennen wir sie etwas anders, um Verwirrung zu vermeiden

 application_train = pd.read_csv(PATH+"application_train.csv") application_test = pd.read_csv(PATH+"application_test.csv") 

Wir werden auch einige Funktionen benötigen, um die Verteilungen und ihren Einfluss auf die Zielvariable schön darzustellen. Vielen Dank an sie fĂŒr den Autor dieses Kernels

 def plot_stats(feature,label_rotation=False,horizontal_layout=True): temp = application_train[feature].value_counts() df1 = pd.DataFrame({feature: temp.index,' ': temp.values})​ #   target=1   cat_perc = application_train[[feature, 'TARGET']].groupby([feature],as_index=False).mean() cat_perc.sort_values(by='TARGET', ascending=False, inplace=True) if(horizontal_layout): fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(12,6)) else: fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(12,14)) sns.set_color_codes("pastel") s = sns.barplot(ax=ax1, x = feature, y=" ",data=df1) if(label_rotation): s.set_xticklabels(s.get_xticklabels(),rotation=90) s = sns.barplot(ax=ax2, x = feature, y='TARGET', order=cat_perc[feature], data=cat_perc) if(label_rotation): s.set_xticklabels(s.get_xticklabels(),rotation=90) plt.ylabel(' ', fontsize=10) plt.tick_params(axis='both', which='major', labelsize=10)​ plt.show(); 

Wir werden also die Hauptzeichen der Kunden berĂŒcksichtigen

Art des Darlehens


 plot_stats('NAME_CONTRACT_TYPE') 



Interessanterweise machen revolvierende Kredite (wahrscheinlich Überziehungskredite oder Ă€hnliches) weniger als 10% der Gesamtzahl der Kredite aus. Gleichzeitig ist der Prozentsatz der Nichtrendite unter ihnen viel höher. Ein guter Grund, die Arbeitsweise dieser Kredite zu ĂŒberarbeiten und sie vielleicht sogar aufzugeben.

Geschlecht des Kunden


 plot_stats('CODE_GENDER') 



Es gibt fast doppelt so viele weibliche Klienten wie MÀnner, wobei MÀnner ein viel höheres Risiko aufweisen.

Auto- und Eigentum


 plot_stats('FLAG_OWN_CAR') plot_stats('FLAG_OWN_REALTY') 




Kunden mit dem Auto sind halb so viel wie "pferdelos". Das Risiko ist fast gleich, Kunden mit der Maschine zahlen etwas besser.

Bei Immobilien ist das Gegenteil der Fall - es gibt halb so wenige Kunden ohne diese. Das Risiko fĂŒr Immobilienbesitzer ist ebenfalls etwas geringer.

Familienstand


 plot_stats('NAME_FAMILY_STATUS',True, True) 



WĂ€hrend die meisten Klienten verheiratet sind, sind Zivil- und Alleinklienten die riskantesten. Witwer weisen ein minimales Risiko auf.

Anzahl der Kinder


 plot_stats('CNT_CHILDREN') 



Die meisten Kunden sind kinderlos. Gleichzeitig weisen Kunden mit 9 und 11 Kindern eine vollstĂ€ndige NichtrĂŒckerstattung auf

 application_train.CNT_CHILDREN.value_counts() 

0 215371
1 61119
2 26749
3 3717
4 429
5 84
6 21
7 7
14 3
19 2
12 2
10 2
9 2
8 2
11 1
Name: CNT_CHILDREN, dtype: int64


Wie die Berechnung der Werte zeigt, sind diese Daten statistisch nicht signifikant - nur 1-2 Kunden beider Kategorien. Alle drei gingen jedoch in Verzug, ebenso wie die HĂ€lfte der Kunden mit 6 Kindern.

Anzahl der Familienmitglieder


 plot_stats('CNT_FAM_MEMBERS',True) 



Die Situation ist Ă€hnlich - je weniger MĂŒnder, desto höher die Rendite.

Art des Einkommens


 plot_stats('NAME_INCOME_TYPE',False,False) 



Alleinerziehende MĂŒtter und Arbeitslose werden wahrscheinlich in der Antragsphase abgeschnitten - es gibt zu wenige von ihnen in der Stichprobe. Aber die Probleme zeigen sich stabil.

Art der AktivitÀt


 plot_stats('OCCUPATION_TYPE',True, False) 



 application_train.OCCUPATION_TYPE.value_counts() 

Laborers 55186
Sales staff 32102
Core staff 27570
Managers 21371
Drivers 18603
High skill tech staff 11380
Accountants 9813
Medicine staff 8537
Security staff 6721
Cooking staff 5946
Cleaning staff 4653
Private service staff 2652
Low-skill Laborers 2093
Waiters/barmen staff 1348
Secretaries 1305
Realty agents 751
HR staff 563
IT staff 526
Name: OCCUPATION_TYPE, dtype: int64


Es ist fĂŒr Fahrer und Sicherheitsbeamte von Interesse, die zahlreich sind und hĂ€ufiger Probleme haben als andere Kategorien.

Bildung


 plot_stats('NAME_EDUCATION_TYPE',True) 



Je höher die Ausbildung, desto besser ist natĂŒrlich die Wiederholung.

Art der Organisation - Arbeitgeber


 plot_stats('ORGANIZATION_TYPE',True, False) 



Der höchste Prozentsatz der NichtrĂŒckgabe wird bei Transport: Typ 3 (16%), Branche: Typ 13 (13,5%), Branche: Typ 8 (12,5%) und Restaurant (bis zu 12%) beobachtet.

Kreditvergabe


BerĂŒcksichtigen Sie die Verteilung der KreditbetrĂ€ge und ihre Auswirkungen auf die RĂŒckzahlung

 plt.figure(figsize=(12,5)) plt.title(" AMT_CREDIT") ax = sns.distplot(app_train["AMT_CREDIT"]) 



 plt.figure(figsize=(12,5))​ # KDE ,   sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, 'AMT_CREDIT'], label = 'target == 0')​ # KDE   sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, 'AMT_CREDIT'], label = 'target == 1')​ #  plt.xlabel(' '); plt.ylabel(''); plt.title(' '); 



Wie das Dichtediagramm zeigt, werden robuste Mengen hĂ€ufiger zurĂŒckgegeben

Dichteverteilung


 plt.figure(figsize=(12,5)) plt.title(" REGION_POPULATION_RELATIVE") ax = sns.distplot(app_train["REGION_POPULATION_RELATIVE"]) 



 plt.figure(figsize=(12,5))​ # KDE ,   sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, 'REGION_POPULATION_RELATIVE'], label = 'target == 0')​ # KDE   sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, 'REGION_POPULATION_RELATIVE'], label = 'target == 1')​ #  plt.xlabel(''); plt.ylabel(' '); plt.title(' '); 



Kunden aus bevölkerungsreicheren Regionen zahlen Kredite tendenziell besser.

So erhielten wir eine Vorstellung von den Hauptmerkmalen des Datensatzes und deren Einfluss auf das Ergebnis. Wir werden nichts spezielles mit den in diesem Artikel aufgefĂŒhrten tun, aber sie können sich in zukĂŒnftigen Arbeiten als sehr wichtig herausstellen.

Feature Engineering - Feature-Konvertierung


Wettbewerbe auf Kaggle werden durch Transformation von Zeichen gewonnen - derjenige, der aus den Daten die nĂŒtzlichsten Zeichen erstellen kann, gewinnt. Zumindest fĂŒr strukturierte Daten sind Gewinnmodelle jetzt grundsĂ€tzlich verschiedene Optionen zur Erhöhung des Gradienten. In den meisten FĂ€llen ist es effizienter, Zeit mit der Konvertierung von Attributen zu verbringen, als Hyperparameter einzurichten oder Modelle auszuwĂ€hlen. Ein Modell kann immer noch nur aus den Daten lernen, die an es ĂŒbertragen wurden. Die Hauptverantwortung fĂŒr das Datum des Wissenschaftlers liegt darin, sicherzustellen, dass die Daten fĂŒr die Aufgabe relevant sind.

Der Prozess der Transformation von Merkmalen kann die Erstellung neuer verfĂŒgbarer Daten, die Auswahl der wichtigsten verfĂŒgbaren Daten usw. umfassen. Wir werden diesmal Polynomzeichen versuchen.

Polynomzeichen


Die Polynommethode zum Erstellen von Features besteht darin, dass wir einfach Features erstellen, die dem Grad der verfĂŒgbaren Features und ihren Produkten entsprechen. In einigen FĂ€llen können solche konstruierten Merkmale eine stĂ€rkere Korrelation mit der Zielvariablen aufweisen als ihre „Eltern“. Obwohl solche Methoden hĂ€ufig in statistischen Modellen verwendet werden, sind sie beim maschinellen Lernen viel seltener. Allerdings. Nichts hindert uns daran, sie auszuprobieren, zumal Scikit-Learn eine Klasse speziell fĂŒr diese Zwecke hat - PolynomialFeatures -, die Polynom-Features und ihre Produkte erstellt. Sie mĂŒssen nur die ursprĂŒnglichen Features selbst und den maximalen Grad angeben, in dem sie erhöht werden mĂŒssen. Wir verwenden die stĂ€rksten Effekte auf das Ergebnis von 4 Attributen und Grad 3, um das Modell nicht zu komplizieren und eine Überanpassung zu vermeiden (Übertraining des Modells - seine ĂŒbermĂ€ĂŸige Anpassung an das Trainingsmuster).

 #       poly_features = app_train[['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH', 'TARGET']] poly_features_test = app_test[['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']]​ #    from sklearn.preprocessing import Imputer imputer = Imputer(strategy = 'median')​ poly_target = poly_features['TARGET']​ poly_features = poly_features.drop('TARGET', axis=1)​ poly_features = imputer.fit_transform(poly_features) poly_features_test = imputer.transform(poly_features_test) from sklearn.preprocessing import PolynomialFeatures #     3 poly_transformer = PolynomialFeatures(degree = 3) #    poly_transformer.fit(poly_features) #   poly_features = poly_transformer.transform(poly_features) poly_features_test = poly_transformer.transform(poly_features_test) print('  : ', poly_features.shape) 

: (307511, 35)
get_feature_names


 poly_transformer.get_feature_names(input_features = ['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH'])[:15] 

['1',
'EXT_SOURCE_1',
'EXT_SOURCE_2',
'EXT_SOURCE_3',
'DAYS_BIRTH',
'EXT_SOURCE_1^2',
'EXT_SOURCE_1 EXT_SOURCE_2',
'EXT_SOURCE_1 EXT_SOURCE_3',
'EXT_SOURCE_1 DAYS_BIRTH',
'EXT_SOURCE_2^2',
'EXT_SOURCE_2 EXT_SOURCE_3',
'EXT_SOURCE_2 DAYS_BIRTH',
'EXT_SOURCE_3^2',
'EXT_SOURCE_3 DAYS_BIRTH',
'DAYS_BIRTH^2']


Insgesamt 35 Polynom- und Ableitungsmerkmale. ÜberprĂŒfen Sie ihre Korrelation mit dem Ziel.

 #     poly_features = pd.DataFrame(poly_features, columns = poly_transformer.get_feature_names(['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']))​ #   poly_features['TARGET'] = poly_target​ #   poly_corrs = poly_features.corr()['TARGET'].sort_values()​ #      print(poly_corrs.head(10)) print(poly_corrs.tail(5)) 

EXT_SOURCE_2 EXT_SOURCE_3 -0.193939
EXT_SOURCE_1 EXT_SOURCE_2 EXT_SOURCE_3 -0.189605
EXT_SOURCE_2 EXT_SOURCE_3 DAYS_BIRTH -0.181283
EXT_SOURCE_2^2 EXT_SOURCE_3 -0.176428
EXT_SOURCE_2 EXT_SOURCE_3^2 -0.172282
EXT_SOURCE_1 EXT_SOURCE_2 -0.166625
EXT_SOURCE_1 EXT_SOURCE_3 -0.164065
EXT_SOURCE_2 -0.160295
EXT_SOURCE_2 DAYS_BIRTH -0.156873
EXT_SOURCE_1 EXT_SOURCE_2^2 -0.156867
Name: TARGET, dtype: float64
DAYS_BIRTH -0.078239
DAYS_BIRTH^2 -0.076672
DAYS_BIRTH^3 -0.074273
TARGET 1.000000
1 NaN
Name: TARGET, dtype: float64


Einige Zeichen zeigen also eine höhere Korrelation als das Original. Es ist sinnvoll, mit und ohne sie zu lernen (wie viel mehr beim maschinellen Lernen kann dies experimentell bestimmt werden). Erstellen Sie dazu eine Kopie der Datenrahmen und fĂŒgen Sie dort neue Funktionen hinzu.

 #      poly_features_test = pd.DataFrame(poly_features_test, columns = poly_transformer.get_feature_names(['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']))​ #    poly_features['SK_ID_CURR'] = app_train['SK_ID_CURR'] app_train_poly = app_train.merge(poly_features, on = 'SK_ID_CURR', how = 'left')​ #    poly_features_test['SK_ID_CURR'] = app_test['SK_ID_CURR'] app_test_poly = app_test.merge(poly_features_test, on = 'SK_ID_CURR', how = 'left')​ #   app_train_poly, app_test_poly = app_train_poly.align(app_test_poly, join = 'inner', axis = 1)​ #   print('    : ', app_train_poly.shape) print('    : ', app_test_poly.shape) 

: (307511, 277)
: (48744, 277)


Modelltraining


Grundstufe


In den Berechnungen mĂŒssen Sie von einer grundlegenden Ebene des Modells ausgehen, unter die es nicht mehr möglich ist, zu fallen. In unserem Fall könnte dies fĂŒr alle Testkunden 0,5 sein - dies zeigt, dass wir ĂŒberhaupt keine Ahnung haben, ob der Kunde das Darlehen zurĂŒckzahlen wird oder nicht. In unserem Fall wurden bereits Vorarbeiten durchgefĂŒhrt und komplexere Modelle können verwendet werden.

Logistische Regression


Um die logistische Regression zu berechnen , mĂŒssen wir Tabellen mit codierten kategorialen Merkmalen erstellen, die fehlenden Daten ausfĂŒllen und normalisieren (auf Werte von 0 bis 1 bringen). All dies fĂŒhrt den folgenden Code aus:

 from sklearn.preprocessing import MinMaxScaler, Imputer​ #      if 'TARGET' in app_train: train = app_train.drop(labels = ['TARGET'], axis=1) else: train = app_train.copy() features = list(train.columns)​ #    test = app_test.copy()​ #     imputer = Imputer(strategy = 'median')​ #  scaler = MinMaxScaler(feature_range = (0, 1))​ #    imputer.fit(train)​ #      train = imputer.transform(train) test = imputer.transform(app_test)​ #      scaler.fit(train) train = scaler.transform(train) test = scaler.transform(test)​ print('  : ', train.shape) print('  : ', test.shape) 

: (307511, 242)
: (48744, 242)


Wir verwenden die logistische Regression von Scikit-Learn als erstes Modell. Nehmen wir das Entlaubungsmodell mit einer Korrektur - wir senken den Regularisierungsparameter C, um eine Überanpassung zu vermeiden. Die Syntax ist normal - wir erstellen ein Modell, trainieren es und prognostizieren die Wahrscheinlichkeit mit Predict_Proba (wir brauchen Wahrscheinlichkeit, nicht 0/1).

 from sklearn.linear_model import LogisticRegression​ #   log_reg = LogisticRegression(C = 0.0001)​ #   log_reg.fit(train, train_labels) LogisticRegression(C=0.0001, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, solver='liblinear', tol=0.0001, verbose=0, warm_start=False)      .  prdict_proba     mx 2,  m -  ,   -  0,  -  1.    ( ). log_reg_pred = log_reg.predict_proba(test)[:, 1] 

Jetzt können Sie eine Datei zum Hochladen auf Kaggle erstellen. Erstellen Sie einen Datenrahmen aus der Kunden-ID und der Wahrscheinlichkeit einer NichtrĂŒckgabe und laden Sie ihn hoch.

 submit = app_test[['SK_ID_CURR']] submit['TARGET'] = log_reg_pred​ submit.head() 

SK_ID_CURR TARGET
0 100001 0.087954
1 100005 0.163151
2 100013 0.109923
3 100028 0.077124
4 100038 0.151694


 submit.to_csv('log_reg_baseline.csv', index = False) 

Das Ergebnis unserer titanischen Arbeit: 0,673, mit dem besten Ergebnis fĂŒr heute ist 0,802.

Verbessertes Modell - ZufÀlliger Wald


Logreg zeigt sich nicht sehr gut. Versuchen wir, ein verbessertes Modell zu verwenden - einen zufĂ€lligen Wald. Dies ist ein viel leistungsfĂ€higeres Modell, das Hunderte von BĂ€umen bauen und ein viel genaueres Ergebnis erzielen kann. Wir benutzen 100 BĂ€ume. Das Schema der Arbeit mit dem Modell ist das gleiche, völlig standardmĂ€ĂŸige - Laden des Klassifikators, Training. Vorhersage.

 from sklearn.ensemble import RandomForestClassifier​ #   random_forest = RandomForestClassifier(n_estimators = 100, random_state = 50)​ #     random_forest.fit(train, train_labels)​ #     predictions = random_forest.predict_proba(test)[:, 1]​ #     submit = app_test[['SK_ID_CURR']] submit['TARGET'] = predictions​ #  submit.to_csv('random_forest_baseline.csv', index = False) 


Das zufÀllige Waldergebnis ist etwas besser - 0,683

Trainingsmodell mit Polynommerkmalen


Jetzt haben wir ein Modell. Das macht zumindest etwas - es ist Zeit, unsere Polynomzeichen zu testen. Machen wir dasselbe mit ihnen und vergleichen wir das Ergebnis.

 poly_features_names = list(app_train_poly.columns)​ #         imputer = Imputer(strategy = 'median')​ poly_features = imputer.fit_transform(app_train_poly) poly_features_test = imputer.transform(app_test_poly)​ #  scaler = MinMaxScaler(feature_range = (0, 1))​ poly_features = scaler.fit_transform(poly_features) poly_features_test = scaler.transform(poly_features_test)​ random_forest_poly = RandomForestClassifier(n_estimators = 100, random_state = 50) #     random_forest_poly.fit(poly_features, train_labels)​ #  predictions = random_forest_poly.predict_proba(poly_features_test)[:, 1]​ #    submit = app_test[['SK_ID_CURR']] submit['TARGET'] = predictions​ #   submit.to_csv('random_forest_baseline_engineered.csv', index = False) 


Das Ergebnis eines zufÀlligen Waldes mit Polynommerkmalen ist schlechter geworden - 0,633. Was die Notwendigkeit ihrer Verwendung stark in Frage stellt.

GradientenverstÀrkung


— « » . «» . .

 from lightgbm import LGBMClassifier​ clf = LGBMClassifier() clf.fit(train, train_labels)​ predictions = clf.predict_proba(test)[:, 1]​ #    submit = app_test[['SK_ID_CURR']] submit['TARGET'] = predictions​ #   submit.to_csv('lightgbm_baseline.csv', index = False) 


LightGBM — 0,735,

—


— ( ). , , .

 #      def show_feature_importances(model, features): plt.figure(figsize = (12, 8)) #          results = pd.DataFrame({'feature': features, 'importance': model.feature_importances_}) results = results.sort_values('importance', ascending = False) #  print(results.head(10)) print('\n     0.01 = ', np.sum(results['importance'] > 0.01)) #  results.head(20).plot(x = 'feature', y = 'importance', kind = 'barh', color = 'red', edgecolor = 'k', title = 'Feature Importances'); return results #         feature_importances = show_feature_importances(clf, features) 

​ feature importance
28 EXT_SOURCE_1 310
30 EXT_SOURCE_3 282
29 EXT_SOURCE_2 271
7 DAYS_BIRTH 192
3 AMT_CREDIT 161
4 AMT_ANNUITY 142
5 AMT_GOODS_PRICE 129
8 DAYS_EMPLOYED 127
10 DAYS_ID_PUBLISH 102
9 DAYS_REGISTRATION 69

0.01 = 158




, 4 . — , ,

HinzufĂŒgen von Daten aus anderen Tabellen


Jetzt werden wir sorgfĂ€ltig ĂŒber zusĂ€tzliche Tabellen nachdenken und darĂŒber, was damit gemacht werden kann. Beginnen Sie sofort mit der Vorbereitung der Tische fĂŒr das weitere Training. Löschen Sie jedoch zuerst die umfangreichen Tabellen der Vergangenheit aus dem Speicher, löschen Sie den Speicher mit dem Garbage Collector und importieren Sie die fĂŒr die weitere Analyse erforderlichen Bibliotheken.

 import gc​ #del app_train, app_test, train_labels, application_train, application_test, poly_features, poly_features_test​ gc.collect() import pandas as pd import numpy as np​ from sklearn.preprocessing import MinMaxScaler, LabelEncoder from sklearn.model_selection import train_test_split, KFold from sklearn.metrics import accuracy_score, roc_auc_score, confusion_matrix from sklearn.feature_selection import VarianceThreshold​ from lightgbm import LGBMClassifier 

Importieren Sie Daten und entfernen Sie sofort die Zielspalte in einer separaten Spalte

 data = pd.read_csv('../input/application_train.csv') test = pd.read_csv('../input/application_test.csv') prev = pd.read_csv('../input/previous_application.csv') buro = pd.read_csv('../input/bureau.csv') buro_balance = pd.read_csv('../input/bureau_balance.csv') credit_card = pd.read_csv('../input/credit_card_balance.csv') POS_CASH = pd.read_csv('../input/POS_CASH_balance.csv') payments = pd.read_csv('../input/installments_payments.csv')​ #Separate target variable y = data['TARGET'] del data['TARGET'] 

Codieren Sie sofort kategoriale Features. Wir haben dies bereits zuvor getan und die Trainings- und Testmuster separat codiert und dann die Daten ausgerichtet. Versuchen wir einen etwas anderen Ansatz - wir werden alle diese kategorialen Zeichen finden, die Datenrahmen kombinieren, aus der Liste der gefundenen kodieren und dann die Stichproben erneut in Trainings- und Testmuster unterteilen.

 categorical_features = [col for col in data.columns if data[col].dtype == 'object']​ one_hot_df = pd.concat([data,test]) one_hot_df = pd.get_dummies(one_hot_df, columns=categorical_features)​ data = one_hot_df.iloc[:data.shape[0],:] test = one_hot_df.iloc[data.shape[0]:,]​ print ('  ', data.shape) print ('  ', test.shape) 

(307511, 245)
(48744, 245)


KreditbĂŒrodaten zum monatlichen Kreditsaldo.


 buro_balance.head() 



MONTHS_BALANCE - Die Anzahl der Monate vor dem Datum der Beantragung eines Darlehens. Schauen Sie sich die "Status" genauer an.

 buro_balance.STATUS.value_counts() 

C 13646993
0 7499507
X 5810482
1 242347
5 62406
2 23419
3 8924
4 5847
Name: STATUS, dtype: int64


Status bedeuten Folgendes:

- geschlossen, dh zurĂŒckgezahltes Darlehen. X ist ein unbekannter Status. 0 - aktuelles Darlehen, keine KriminalitĂ€t. 1 - Verzögerung von 1-30 Tagen, 2 - Verzögerung von 31-60 Tagen usw. bis Status 5 - Das Darlehen wird an einen Dritten verkauft oder abgeschrieben.

Hier können beispielsweise folgende Zeichen unterschieden werden: buro_grouped_size - die Anzahl der EintrÀge in der Datenbank buro_grouped_max - der maximale Kreditsaldo buro_grouped_min - der minimale Kreditsaldo

Und all diese Kreditstatus können codiert werden (wir verwenden die Unstack-Methode und hÀngen die empfangenen Daten seitdem an die Buro-Tabelle an SK_ID_BUREAU ist hier und da gleich.

 buro_grouped_size = buro_balance.groupby('SK_ID_BUREAU')['MONTHS_BALANCE'].size() buro_grouped_max = buro_balance.groupby('SK_ID_BUREAU')['MONTHS_BALANCE'].max() buro_grouped_min = buro_balance.groupby('SK_ID_BUREAU')['MONTHS_BALANCE'].min()​ buro_counts = buro_balance.groupby('SK_ID_BUREAU')['STATUS'].value_counts(normalize = False) buro_counts_unstacked = buro_counts.unstack('STATUS') buro_counts_unstacked.columns = ['STATUS_0', 'STATUS_1','STATUS_2','STATUS_3','STATUS_4','STATUS_5','STATUS_C','STATUS_X',] buro_counts_unstacked['MONTHS_COUNT'] = buro_grouped_size buro_counts_unstacked['MONTHS_MIN'] = buro_grouped_min buro_counts_unstacked['MONTHS_MAX'] = buro_grouped_max​ buro = buro.join(buro_counts_unstacked, how='left', on='SK_ID_BUREAU') del buro_balance gc.collect() 

Allgemeine Informationen zu Kreditauskunfteien


 buro.head() 


(Die ersten 7 Spalten werden angezeigt.) Es gibt eine

ganze Reihe von Daten, die Sie im Allgemeinen einfach mit One-Hot-Encoding codieren können, gruppieren nach SK_ID_CURR, durchschnittlich und auf die gleiche Weise fĂŒr den Beitritt zur Haupttabelle vorbereiten

 buro_cat_features = [bcol for bcol in buro.columns if buro[bcol].dtype == 'object'] buro = pd.get_dummies(buro, columns=buro_cat_features) avg_buro = buro.groupby('SK_ID_CURR').mean() avg_buro['buro_count'] = buro[['SK_ID_BUREAU', 'SK_ID_CURR']].groupby('SK_ID_CURR').count()['SK_ID_BUREAU'] del avg_buro['SK_ID_BUREAU'] del buro gc.collect() 


Daten zu frĂŒheren Anwendungen


 prev.head() 



In Ă€hnlicher Weise codieren wir kategoriale Merkmale, mitteln und kombinieren sie ĂŒber die aktuelle ID.

 prev_cat_features = [pcol for pcol in prev.columns if prev[pcol].dtype == 'object'] prev = pd.get_dummies(prev, columns=prev_cat_features) avg_prev = prev.groupby('SK_ID_CURR').mean() cnt_prev = prev[['SK_ID_CURR', 'SK_ID_PREV']].groupby('SK_ID_CURR').count() avg_prev['nb_app'] = cnt_prev['SK_ID_PREV'] del avg_prev['SK_ID_PREV'] del prev gc.collect() 


Kreditkartenguthaben


 POS_CASH.head() 



 POS_CASH.NAME_CONTRACT_STATUS.value_counts() 

Active 9151119
Completed 744883
Signed 87260
Demand 7065
Returned to the store 5461
Approved 4917
Amortized debt 636
Canceled 15
XNA 2
Name: NAME_CONTRACT_STATUS, dtype: int64


Wir codieren kategoriale Merkmale und bereiten eine Tabelle zum Kombinieren vor

 le = LabelEncoder() POS_CASH['NAME_CONTRACT_STATUS'] = le.fit_transform(POS_CASH['NAME_CONTRACT_STATUS'].astype(str)) nunique_status = POS_CASH[['SK_ID_CURR', 'NAME_CONTRACT_STATUS']].groupby('SK_ID_CURR').nunique() nunique_status2 = POS_CASH[['SK_ID_CURR', 'NAME_CONTRACT_STATUS']].groupby('SK_ID_CURR').max() POS_CASH['NUNIQUE_STATUS'] = nunique_status['NAME_CONTRACT_STATUS'] POS_CASH['NUNIQUE_STATUS2'] = nunique_status2['NAME_CONTRACT_STATUS'] POS_CASH.drop(['SK_ID_PREV', 'NAME_CONTRACT_STATUS'], axis=1, inplace=True) 

Kartendaten


 credit_card.head() 


(erste 7 Spalten)

Ähnliche Arbeit

 credit_card['NAME_CONTRACT_STATUS'] = le.fit_transform(credit_card['NAME_CONTRACT_STATUS'].astype(str)) nunique_status = credit_card[['SK_ID_CURR', 'NAME_CONTRACT_STATUS']].groupby('SK_ID_CURR').nunique() nunique_status2 = credit_card[['SK_ID_CURR', 'NAME_CONTRACT_STATUS']].groupby('SK_ID_CURR').max() credit_card['NUNIQUE_STATUS'] = nunique_status['NAME_CONTRACT_STATUS'] credit_card['NUNIQUE_STATUS2'] = nunique_status2['NAME_CONTRACT_STATUS'] credit_card.drop(['SK_ID_PREV', 'NAME_CONTRACT_STATUS'], axis=1, inplace=True) 

Zahlungsdaten


 payments.head() 


(Die ersten 7 Spalten werden angezeigt.)

Erstellen wir drei Tabellen - mit Durchschnitts-, Minimal- und Maximalwerten aus dieser Tabelle.

 avg_payments = payments.groupby('SK_ID_CURR').mean() avg_payments2 = payments.groupby('SK_ID_CURR').max() avg_payments3 = payments.groupby('SK_ID_CURR').min() del avg_payments['SK_ID_PREV'] del payments gc.collect() 

TabellenverknĂŒpfung


 data = data.merge(right=avg_prev.reset_index(), how='left', on='SK_ID_CURR') test = test.merge(right=avg_prev.reset_index(), how='left', on='SK_ID_CURR')​ data = data.merge(right=avg_buro.reset_index(), how='left', on='SK_ID_CURR') test = test.merge(right=avg_buro.reset_index(), how='left', on='SK_ID_CURR')​ data = data.merge(POS_CASH.groupby('SK_ID_CURR').mean().reset_index(), how='left', on='SK_ID_CURR') test = test.merge(POS_CASH.groupby('SK_ID_CURR').mean().reset_index(), how='left', on='SK_ID_CURR')​ data = data.merge(credit_card.groupby('SK_ID_CURR').mean().reset_index(), how='left', on='SK_ID_CURR') test = test.merge(credit_card.groupby('SK_ID_CURR').mean().reset_index(), how='left', on='SK_ID_CURR')​ data = data.merge(right=avg_payments.reset_index(), how='left', on='SK_ID_CURR') test = test.merge(right=avg_payments.reset_index(), how='left', on='SK_ID_CURR')​ data = data.merge(right=avg_payments2.reset_index(), how='left', on='SK_ID_CURR') test = test.merge(right=avg_payments2.reset_index(), how='left', on='SK_ID_CURR')​ data = data.merge(right=avg_payments3.reset_index(), how='left', on='SK_ID_CURR') test = test.merge(right=avg_payments3.reset_index(), how='left', on='SK_ID_CURR') del avg_prev, avg_buro, POS_CASH, credit_card, avg_payments, avg_payments2, avg_payments3 gc.collect() print ('  ', data.shape) print ('  ', test.shape) print ('  ', y.shape) 

(307511, 504)
(48744, 504)
(307511,)


Und tatsÀchlich werden wir diesen doppelten Tisch mit einem Gradienten-Boost treffen!

 from lightgbm import LGBMClassifier​ clf2 = LGBMClassifier() clf2.fit(data, y)​ predictions = clf2.predict_proba(test)[:, 1]​ #    submission = test[['SK_ID_CURR']] submission['TARGET'] = predictions​ #   submission.to_csv('lightgbm_full.csv', index = False) 

das Ergebnis ist 0,770.

OK, zum Schluss versuchen wir eine komplexere Technik mit Falten in Falten, Kreuzvalidierung und Auswahl der besten Iteration.

 folds = KFold(n_splits=5, shuffle=True, random_state=546789) oof_preds = np.zeros(data.shape[0]) sub_preds = np.zeros(test.shape[0])​ feature_importance_df = pd.DataFrame()​ feats = [f for f in data.columns if f not in ['SK_ID_CURR']]​ for n_fold, (trn_idx, val_idx) in enumerate(folds.split(data)): trn_x, trn_y = data[feats].iloc[trn_idx], y.iloc[trn_idx] val_x, val_y = data[feats].iloc[val_idx], y.iloc[val_idx] clf = LGBMClassifier( n_estimators=10000, learning_rate=0.03, num_leaves=34, colsample_bytree=0.9, subsample=0.8, max_depth=8, reg_alpha=.1, reg_lambda=.1, min_split_gain=.01, min_child_weight=375, silent=-1, verbose=-1, ) clf.fit(trn_x, trn_y, eval_set= [(trn_x, trn_y), (val_x, val_y)], eval_metric='auc', verbose=100, early_stopping_rounds=100 #30 ) oof_preds[val_idx] = clf.predict_proba(val_x, num_iteration=clf.best_iteration_)[:, 1] sub_preds += clf.predict_proba(test[feats], num_iteration=clf.best_iteration_)[:, 1] / folds.n_splits fold_importance_df = pd.DataFrame() fold_importance_df["feature"] = feats fold_importance_df["importance"] = clf.feature_importances_ fold_importance_df["fold"] = n_fold + 1 feature_importance_df = pd.concat([feature_importance_df, fold_importance_df], axis=0) print('Fold %2d AUC : %.6f' % (n_fold + 1, roc_auc_score(val_y, oof_preds[val_idx]))) del clf, trn_x, trn_y, val_x, val_y gc.collect()​ print('Full AUC score %.6f' % roc_auc_score(y, oof_preds))​ test['TARGET'] = sub_preds​ test[['SK_ID_CURR', 'TARGET']].to_csv('submission_cross.csv', index=False) 

Full AUC score 0.785845

Letzter Treffer bei Kaggle 0.783

Wohin als nÀchstes gehen


Arbeiten Sie auf jeden Fall weiter mit Schildern. Durchsuchen Sie die Daten, wÀhlen Sie einige der Zeichen aus, kombinieren Sie sie und hÀngen Sie zusÀtzliche Tabellen auf andere Weise an. Sie können mit Hyperparametern Mogheli experimentieren - viele Richtungen.

Ich hoffe, diese kleine Zusammenstellung hat Ihnen moderne Methoden zur Erforschung von Daten und zur Erstellung von Vorhersagemodellen gezeigt. Lerne Daten, nimm an Wettbewerben teil, sei cool!

Und wieder Links zu den Kerneln, die mir bei der Vorbereitung dieses Artikels geholfen haben. Der Artikel wird auch in Form eines Laptops auf Github veröffentlicht. Sie können ihn herunterladen, datieren und ausfĂŒhren und experimentieren.

Will Koehrsen. Beginnen Sie hier: Eine sanfte EinfĂŒhrung
sban. HomeCreditRisk: Umfangreiche EDA + Baseline [0.772]
Gabriel Preda. Home Credit Default Risk Extensive EDA
Pavan Raj. Loan repayers v/s Loan defaulters — HOME CREDIT
Lem Lordje Ko. 15 lines: Just EXT_SOURCE_x
Shanth. HOME CREDIT — BUREAU DATA — FEATURE ENGINEERING
Dmitriy Kisil. Good_fun_with_LigthGBM

Source: https://habr.com/ru/post/de414613/


All Articles