[PYTHON] SIGNATE [1st _Beginner Limited Competition] Lösen von Bankkunden-Targeting

Einführung

Bei SIGNATE, einer der inländischen Plattformen für Wettbewerbe zum maschinellen Lernen, nahm ich an der im August 2020 abgehaltenen "[1st _Beginner Limited Competition] Bank Customer Targeting" teil und verwendete daher auch mein eigenes Memorandum als Lösung. Ich werde es beschreiben. ** Außerdem haben wir keine besonders originelle Lösung. Ich hoffe, es ist hilfreich für Anfänger im maschinellen Lernen ** (der Text ist lang).

Begrenzter Wettbewerb für Anfänger

In SIGNATE werden Titel gemäß den Ergebnissen des Wettbewerbs vergeben, aber der Titel zum Zeitpunkt der Registrierung in SIGNATE lautet "Begginer". Dieser Wettbewerb war ein Wettbewerb, an dem nur Personen in der untersten Anfängerklasse teilnehmen konnten (es scheint, dass dies das erste Mal für den begrenzten Beginer-Wettbewerb ist).

Normalerweise nehmen Sie am Wettbewerb um den nächsten Titel "Intermediate from Beginner" teil. Wenn Sie einmal die besten 60% erreichen, werden Sie befördert. Wenn Sie in diesem Wettbewerb jedoch die angegebene Punktzahl erreichen, werden Sie zu diesem Zeitpunkt automatisch zum Intermediate befördert. Es wird ein Wettbewerb in diesem Sinne sein.

Ich habe auch nur SIGNATE registriert und war ein Anfänger, also habe ich teilgenommen.

piramid.png

Wettbewerbsübersicht

Als Ergebnis einer von einer Bank durchgeführten Kampagne wird anhand von Kundenattributdaten und Kontaktinformationen aus früheren Kampagnen vorhergesagt, ob ein Kunde ein Konto eröffnet hat oder nicht. Dies ist ein sogenanntes "Klassifizierungs" -Problem beim maschinellen Lernen.

Die bereitgestellten Daten sind wie folgt. Die Zugdaten waren 27100 Datensätze und die Testdaten waren 18050 Datensätze.

Säule Headername Datentyp Erläuterung
0 id int Seriennummer der Leitung
1 age int Alter
2 job varchar Besetzung
3 marital varchar Unverheiratet/verheiratet
4 education varchar Bildungsniveau
5 default varchar Gibt es eine Standardeinstellung (ja), no)
6 balance int Jährlicher Durchschnittssaldo (€)
7 housing varchar Wohnungsbaudarlehen (ja), no)
8 loan varchar Privatkredit (ja), no)
9 contact varchar Kontaktmöglichkeit
10 day int Letzter Kontakttermin
11 month char Letzter Kontaktmonat
12 duration int Letzte Kontaktzeit (Sekunden)
13 compaign int Anzahl der Kontakte in der aktuellen Kampagne
14 pdays int Verstrichene Tage: Tage nach Kontakt mit der vorherigen Kampagne
15 previous int Kontaktdatensatz: Anzahl der Kontakte mit Kunden vor der aktuellen Kampagne
16 poutcome varchar Ergebnisse der vorherigen Kampagne
17 y boolean Ob eine feste Anzahlung beantragt werden soll oder nicht (1:Ja, 0:Keiner)

Ausführungsumgebung

OS: Windows10 Prozessor: Core i7 5500U Speicher: 16 GB Anaconda3-Umgebung (Python 3.7.6)

Verzeichnisaufbau

Bank_Prediction  ├ notebook/ ●●●.ipynb  ├ input/ train.csv、test.csv └ Ausgabe / Ausgabe des Vorhersageergebnisses hier

Ablauf der prädiktiven Modellerstellung

Erstellen Sie ein Vorhersagemodell in der folgenden Reihenfolge.

    1. EDA (Exploratory Data Analysis)
  1. Datenvorverarbeitung
    1. Lernen und Vorhersagen Vier. Ergebnis

1. 1. EDA (Exploratory Data Analysis)

Zunächst führen wir eine Analyse durch, um die Struktur und Eigenschaften der angegebenen Daten zu bestätigen. Der Einfachheit halber werde ich in dem Artikel das EDA-Ergebnis der Testdaten weglassen.

Daten lesen

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pandas.plotting import scatter_matrix
import seaborn as sns

#Stellen Sie die maximale Anzahl von Anzeigespalten auf 50 ein
pd.set_option('display.max_columns', 50)

#Verschiedene Daten lesen
train = pd.read_csv("../input/train.csv")
test = pd.read_csv("../input/test.csv")

Bestätigung der Anzahl der Datensätze, der Anzahl der Features, des Datentyps und des Vorhandenseins fehlender Werte

train.info()

info.png Die Anzahl der Datensätze beträgt 27100, die Anzahl der Features beträgt 18 und es ist klar, welche Features numerische Variablen und kategoriale Variablen sind. Es scheint auch, dass diesmal keine fehlenden Werte in den Daten vorhanden sind. Da die Daten diesmal wie die für den Wettbewerb erstellten Daten sind, handelt es sich um schöne Daten ohne fehlende Werte. Bei realitätsbasierten Daten ist es jedoch üblich, eine komplementäre Verarbeitung mit vielen fehlenden Werten durchzuführen.

Bestätigung der Basisstatistik

train.describe()

describe.png

Überprüfen Sie das Histogramm jeder Funktion

train.hist(figsize=(20,20), color='r')

histgram.png Obwohl y angibt, ob ein Konto eröffnet wurde oder nicht, ist ersichtlich, dass die Anzahl der geöffneten (1) im Vergleich zur Anzahl der nicht geöffneten (0) sehr gering ist, was zu unausgeglichenen Daten führt.

Korrelationskoeffizient

colormap = plt.cm.RdBu
plt.figure(figsize=(14,12))
plt.title('Pearson Correlation of Features', y=1.05, size=15)
sns.heatmap(train.select_dtypes(exclude='object').astype(int).corr(),linewidths=0.1,vmax=1.0, vmin=-1.0, 
            square=True, cmap=colormap, linecolor='white', annot=True)

corr.png Unter den Feature-Mengen scheint die vorherige (die Anzahl der Kontakte mit Kunden bisher) die größte Korrelation damit zu haben, ob ein Konto eröffnet wurde oder nicht.

Bestätigung der Verteilung zwischen Merkmalsmengen


g = sns.pairplot(train, hue='y', palette = 'seismic',size=1.2,diag_kind = 'kde',diag_kws=dict(shade=True),plot_kws=dict(s=10) )
g.set(xticklabels=[])

histgram2.png

Blau ist die Verteilung von Kunden, die kein Konto eröffnet haben, und Rot ist die Verteilung von Kunden, die ein Konto eröffnet haben. Betrachtet man das Alter des diagonalen Histogramms, so scheint es, dass jüngere Menschen eher kein Konto eröffnen. Es gibt auch einen Unterschied in der Verteilung am Tag (letzter Kontakttag).

Überprüfen Sie die Anzahl der Elemente in jeder kategorialen Variablen


for _ in range(len(train.select_dtypes(include='object').columns)):
    print(train.select_dtypes(include='object').columns.values[_])
    print(len(train.select_dtypes(include='object').iloc[:,_].value_counts().to_dict()))
    print(train.select_dtypes(include='object').iloc[:,_].value_counts().to_dict())

category.png Ich konnte die Anzahl der Elemente in jeder kategorialen Variablen bestätigen.

2. Datenvorverarbeitung

Von hier aus führen wir eine Datenvorverarbeitung durch, um ein Vorhersagemodell zu erstellen.

Hinzufügen von Funktionen

Zunächst haben wir für kategoriale Variablen eine Funktion hinzugefügt, die drei Funktionen für Kredite kombiniert.

Funktionen wurden auch für numerische Variablen hinzugefügt. Hier haben wir die Feature-Menge hinzugefügt, die die Differenz zwischen dem Durchschnitt jeder vorhandenen Feature-Menge und der Feature-Menge jedes Datensatzes darstellt. Es scheint, dass die Generalisierungsleistung durch Hinzufügen einer quadratischen Merkmalsmenge oder einer gewürfelten Merkmalsmenge als neue Merkmalsmenge verbessert werden kann, aber diesmal habe ich es nicht versucht.

#Zugdaten und Testdaten zusammenführen
train2 =  pd.concat([train,test],axis=0)

#Hinzufügen von Funktionen
train2['default_housing_loan'] = train2['default'].str.cat([train2['housing'],train2['loan']], sep='_')
train2['age_median'] = train2['age'] - train2['age'].median()
train2['day_median'] = train2['day'] - train2['day'].median()
train2['duration_median'] = train2['duration'] - train2['duration'].median()
train2['campaign_median'] = train2['campaign'] - train2['campaign'].median()
train2['previous_median'] = train2['previous'] - train2['previous'].median()

Label Encoding Kategoriale Variablen können nicht als Trainingsdaten in das Vorhersagemodell eingegeben werden, daher müssen sie codiert werden. Es gibt verschiedene Codierungsmethoden, aber da der diesmal für das Training verwendete Algorithmus ein Gradientenverstärkungsbaum ist, wird die Label-Codierung verwendet (One-Hot-Codierung ist besser, wenn Probleme wie "Regression" gelöst werden).

Das Folgende ist ein Beispiel für die Etikettencodierung der ehelichen Merkmalsmenge.

married → 0 single → 1 divorced → 2

#Label Encoding
from sklearn.preprocessing import LabelEncoder

category = train2.select_dtypes(include='object')

for col in list(category):
  le = LabelEncoder()
  le.fit(train2[col])
  le.transform(train2[col])
  train2[col] = le.transform(train2[col])

3. 3. Lernen und Vorhersagen

Nachdem die Datenvorverarbeitung für die angegebenen Daten abgeschlossen ist, werden wir weiter trainieren und Vorhersagen treffen. Der für das Training verwendete Algorithmus ist LightGBM. Dieses Mal wurden 20 Modelle erstellt, indem die Zufallszahlen bei der Aufteilung in Trainingsdaten und Verifizierungsdaten geändert wurden, und der Durchschnitt jedes vorhergesagten Werts wurde als endgültiges Vorhersageergebnis (Random Seed Average) verwendet. Die Hyperparameter werden von Oputuna eingestellt.

~~ Außerdem habe ich ** wegen unausgeglichener Daten in den Parametern von LightGBM ** "'class_weight': 'ausgeglichen'" angegeben. ~~ ** (Korrektur) AUC war nicht erforderlich, da es sich um einen Bewertungsindex handelt, der nicht von Datenverzerrungen betroffen ist. Außerdem konnte LightGBM Classiefier class_weight als Parameter angeben. ** ** **

train&predict



#Lightgbm importieren
import optuna.integration.lightgbm as lgb #High Para Tuning mit Optuna
from sklearn.model_selection import  train_test_split
import datetime

#Teilen Sie den zusammengeführten Zug2 in Zug und testen Sie erneut
train = train2[:27100]
test = train2[27100:].drop(['y'],axis=1)

#Holen Sie sich die Werte der objektiven und erklärenden Variablen des Zuges
target = train['y'].values
features = train.drop(['id','y'],axis=1).values

#Testdaten
test_X = test.drop(['id'],axis=1).values

lgb_params = {'objective': 'binary',
              'metric': 'auc', #Der vom Wettbewerb festgelegte Bewertungsindex ist AUC
              #'class_weight': 'balanced' #Ich habe es hier nicht gebraucht
             }

#Durchschnitt der zufälligen Samen 20 Mal
for _ in range(20):

    #Teilen Sie den Zug in Trainingsdaten und Verifizierungsdaten auf
    (features , val_X , target , val_y) = train_test_split(features, target , test_size = 0.2)


    #Erstellen eines Datensatzes für LightGBM
    lgb_train = lgb.Dataset(features, target,feature_name = list(train.drop(['id','y'],axis=1))) #Zum Lernen
    lgb_eval = lgb.Dataset(val_X, val_y, reference=lgb_train) #Zum Boosten
    
    #Festlegen kategorialer Variablen
    categorical_features = ['job', 'marital', 'education', 'default', 'balance','month',
                            'housing', 'loan','poutcome', 'default_housing_loan']

    #Lernen
    model = lgb.train(lgb_params, lgb_train, valid_sets=lgb_eval,
                      categorical_feature = categorical_features,
                      num_boost_round=1000,
                      early_stopping_rounds=20,
                      verbose_eval=10)

    pred = model.predict(test_X) #Wahrscheinlichkeitswert der Kontoanwendung
    
    #Speichern Sie jedes Vorhersageergebnis
    if _ == 0:
        output = pd.DataFrame(pred,columns=['pred' + str(_+1)])
        output2 = output
    
    else:
        output2 = pd.concat([output2,output],axis=1)
    
    #Ende von für

#Durchschnitt jedes Vorhersageergebnis
df_mean = output2.mean(axis='columns')
df_result = pd.concat([test['id'],df_mean],axis=1)

#Export mit der an den Dateinamen angehängten Zeit
now = datetime.datetime.now()
df_result.to_csv('../output/submission' + now.strftime('%Y%m%d_%H%M%S') + '.csv',index=None,header=None)

Vier. Ergebnis

Die im Wettbewerb angegebene Punktzahl (AUC) betrug 0,85, aber meine ** Endpunktzahl betrug 0,855 **. Ich wurde erfolgreich zum Intermediate befördert. ** Die endgültige Rangliste war 62. von 787 Personen **, was weder schlecht noch extrem gut war.

Der Übergang der Partitur ist übrigens wie folgt.

** 0,8470: Kein zufälliger Saatgutdurchschnitt ** ↓ (+0.0034) ** 0,8504: Zufälliger Samen-Durchschnitt 5-mal ** ↓ (+0.0035) ~~ ** 0.8539: Angabe von "'class_weight': 'ausgeglichen'" ** ~~ ↓ (+0.0016) ** 0,8555: Zufälliger Samen Durchschnitt 20 mal **

~~ In meinem Fall habe ich das Gefühl, dass die Angabe von "'class_weight': 'ausgeglichen'" ziemlich effektiv war. ~~

Obwohl ich es in dem auf Qiita veröffentlichten Code korrigiert habe, gab es einen schwerwiegenden Fehler, so dass ich das Gefühl habe, dass ich ohne ihn auf etwa 0,857 hätte steigen können (ein wenig enttäuschend).

Übrigens wurde im Forum (Bulletin Board des Wettbewerbs) geschrieben, dass sich die Punktzahl erheblich erhöht, wenn Sie 100-mal Random Seed Average machen. Ich hätte die durchschnittliche Anzahl erhöhen sollen (ich war 10 Stunden lang nicht bereit zu lernen lol).

Umgang mit unausgeglichenen Daten

** (Korrektur) Wie oben beschrieben, ist dieser Bewertungsindex AUC ein Bewertungsindex, der nicht von Datenverzerrungen betroffen ist, sodass diese Zeit nicht berücksichtigt werden musste. Außerdem konnte LightGBM Classiefier class_weight als Parameter angeben. ** ** **

Ich bemerkte, dass die Trainingsdaten diesmal unausgeglichene Daten waren. Wenn Sie mit unausgeglichenen Daten trainieren, können Sie leicht vorhersagen, dass das Vorhersagemodell ein negatives Beispiel ist. Daher ist die folgende Verarbeitung üblich.

    1. Untersuchen Sie die Anzahl der negativen Fälle entsprechend der Anzahl der positiven Fälle
  1. Gewichten Sie die Anzahl der Proben während des Trainings ohne Unterabtastung

Dieses Mal habe ich nicht unterbewertet, sondern 2. Ich habe auf die folgende Seite verwiesen.

Es ist besser, das Klassengewicht festzulegen, wenn voreingenommene Daten in einer zufälligen Gesamtstruktur klassifiziert werden

Wenn Sie eine Unterabtastung von 1 implementieren möchten, ist die folgende Seite hilfreich.

Downsampling + Bagging mit LightGBM - ein Memorandum von u ++

Übrigens hängt meiner Erfahrung nach das Problem davon ab, ob Unterabtastung oder Gewichtung gut ist. Daher wird empfohlen, beide einmal zu versuchen und diejenige mit der besseren Punktzahl zu übernehmen.

Andere Dinge habe ich versucht

Ich habe auch Pseudo Labeling ausprobiert, aber ich habe es nicht verwendet, weil es in diesem Wettbewerb nicht sehr effektiv war.

Nach den Geschichten anderer Personen, die an dem Wettbewerb teilgenommen haben, sind Target Encoding und Stacking nicht sehr effektiv. Es scheint also ein guter Wettbewerb zu sein, Orthodoxe mit einem einzigen Modell anzugreifen.

Ergänzung

Da dieser Wettbewerb in den SIGNATE-Übungen dasselbe Thema hat, können Sie die Daten von der folgenden Seite herunterladen und die Funktionsweise des Codes überprüfen. Wenn Sie es tatsächlich verschieben möchten, bitte.

[Übungsfrage] Bankkunden-Targeting

Schließlich

Obwohl es sich um einen Begginer-Wettbewerb handelte, war es ein sehr lohnender Wettbewerb mit vielen Lernmöglichkeiten. In Zukunft möchte ich Kaggles MoA (Pharmaceutical Dynamics Competition) und ProbSpace's Splatoon Competition herausfordern. Übrigens habe ich mich auch für das vom Minister für Wirtschaft, Handel und Industrie gesponserte AI-Personalentwicklungsprogramm "AI QUEST" beworben. Wenn ich also das Glück habe, es zu bestehen, bin ich jeden Tag beschäftigt.

P.S. Es hat viel Zeit gekostet, das Bild der SIGNATE-Titelpyramide zu zeichnen ...

Recommended Posts

SIGNATE [1st _Beginner Limited Competition] Lösen von Bankkunden-Targeting
SIGNATE [1st _Beginner Limited Competition] Teilnahme am Bankkunden-Targeting
Signate_ Rückblick auf den 1. Beginner Limited Competition
[SIGNATE] Bankkunden-Targeting @ Learning
Signate 2nd _Beginner Limited Competition Review
JOI2019 / 2020 1. Qualifikation 3. Wie man ein Problem und ein B-Problem löst
So lösen Sie simultane lineare Gleichungen
Lösen von Folienrätseln und 15 Rätseln