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).
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.
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) |
OS: Windows10 Prozessor: Core i7 5500U Speicher: 16 GB Anaconda3-Umgebung (Python 3.7.6)
Bank_Prediction ├ notebook/ ●●●.ipynb ├ input/ train.csv、test.csv └ Ausgabe / Ausgabe des Vorhersageergebnisses hier
Erstellen Sie ein Vorhersagemodell in der folgenden Reihenfolge.
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.
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")
train.info()
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.
train.describe()
train.hist(figsize=(20,20), color='r')
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.
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)
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.
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=[])
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).
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())
Ich konnte die Anzahl der Elemente in jeder kategorialen Variablen bestätigen.
Von hier aus führen wir eine Datenvorverarbeitung durch, um ein Vorhersagemodell zu erstellen.
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])
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)
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).
** (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.
Dieses Mal habe ich nicht unterbewertet, sondern 2. Ich habe auf die folgende Seite verwiesen.
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.
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.
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
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