第2回 金融データ活用チャレンジ で最終評価49位となったモデルを紹介する
0. databricksの便利機能
1. 提供いただいた実行環境(MLクラスタ)
1.1. pythonバージョン
1.2. ライブラリバージョン
2. モデル概要
2.1. feature engineering
2.1.1. 年,月,曜日を特徴量(カテゴリカル変数)に入れる
年はclipしない(2074年はそのまま使う)
def setup_date(df):
for column in DATE_COLUMNS:
date_val = pd.to_datetime(df[column])
df['{0}_year'.format(column)] = date_val.dt.year
df['{0}_month'.format(column)] = date_val.dt.month
df['{0}_dow'.format(column)] = date_val.dt.day_of_week
return df
2.1.2. ApprovalDateとDisbursementDateの差日数を特徴量に入れる
def setup_days_between(df):
days = pd.to_datetime(df['ApprovalDate']) - pd.to_datetime(df['DisbursementDate'])
days = (days.dt.days // 30 * 30).clip(lower=-10000,upper=24000)
df['days'] = days.values
return df
2.1.3. 線形回帰モデル(LinearRegressionなど)の戦力化
差日数は決定木系モデルの場合はそのままで使えるが線形回帰系モデルではもう一段階加工しないと戦力にならないので差日数の値でグループ化(groupby)しMIS_Statusの割合と紐づける
具体的には
class DaysProbaTransformer(BaseEstimator, TransformerMixin):
"""
daysの値(目的変数確率値)変換
"""
def __init__(self):
self.na_proba = 0.0
self.proba_dict={}
return
def fit(self, X, y=None):
# 差日付がnanの場合のMIS_Statusの割合を計算
self.na_proba = X[X['NA_Date']==1][TARGET_COLUMN].mean()
grouped = train_prepared.groupby('days')
for key, group in grouped:
self.proba_dict[key] = group[TARGET_COLUMN].mean()
return self
def transform(self, X):
X['days'] = X['days'].apply(lambda x:self.proba_dict[x] if x in self.proba_dict else self.na_proba)
return X
リークにならないよう常に訓練用データのみからMIS_Statusの割合を生成する
検証用データも混ぜてしまうとリーク効果で異常に成績のよい検証結果が出てしまう
2.1.4. 金額のカテゴリ変数化
'DisbursementGross','GrAppv','SBA_Appv'は線形回帰系モデルの戦力化のためにカテゴリ変数化する
ただし分布(ヒストグラム)は生の値は以下のように指数的であるので計算式で説明変数をエンコードし平滑化する
↓↓↓ モジュラー
alpha = train['DisbursementGross'].quantile(0.5)
def modular(x, alpha):
val = ((x - alpha) / (x + alpha)) // 0.01 * 0.01
return np.clip(val, -1.0, 0.75)
train['DisbursementGross'].apply(lambda x:modular(x,alpha)).plot.hist(bins=50)
train['DisbursementGross'].apply(lambda x:np.log(x+1)).plot.hist(bins=50)
2.2. アルゴリズム
2.2.1. アルゴリズムの選定
pycaretでAUCの成績の良い順にアルゴリズムを選定してもらう
2.2.2. ハイパーパラメータチューニング
2.2.3. 選定モデルを使ったstacking ensemble
cbt_mod = make_pipeline(FillNATransformer(CAT_COLUMNS),ChoiceColumnsTransformer(TRAIN_COLUMNS),
CatBoostClassifier(random_state=SEED, verbose=0, min_child_samples=86,cat_features=CAT_COLUMNS,))
lda_mod = make_pipeline(CountUnitTransformer(),DaysProbaTransformer(),MoneyProbaTransformer(),ChoiceColumnsTransformer(TRAIN_COLUMNS_WITH_DAYS),VCTransformer(VC_COLUMNS),
DropByCorrTransformer(),
LinearDiscriminantAnalysis(covariance_estimator=None, n_components=None,
priors=None, shrinkage=0.4, solver='lsqr',
store_covariance=False, tol=0.0001))
lr_mod = make_pipeline(CountUnitTransformer(),DaysProbaTransformer(),MoneyProbaTransformer(),ChoiceColumnsTransformer(TRAIN_COLUMNS_WITH_DAYS),VCTransformer(VC_COLUMNS),
DropByCorrTransformer(),
LogisticRegression(random_state=SEED,max_iter=2000))
lgb_mod = make_pipeline(CountUnitTransformer(),
ModularTransformer(MONEY_COLUMNS),
ChoiceColumnsTransformer(TRAIN_COLUMNS_WITH_DAYS),
VCTransformer(VC_COLUMNS),
LGBMClassifier(random_state=SEED, verbosity=-1, min_child_samples=86))
gbc_mod = make_pipeline(CountUnitTransformer(),
ModularTransformer(MONEY_COLUMNS),
ChoiceColumnsTransformer(TRAIN_COLUMNS_WITH_DAYS),
NMFTransformer('Job',['NoEmp','CreateJob','RetainedJob'],1),
VCTransformer(VC_COLUMNS),FillNATransformer(['days'],na_val=0),
GradientBoostingClassifier(random_state=SEED, max_features='sqrt', min_samples_leaf=86))
ext_mod = make_pipeline(CountUnitTransformer(),
ModularTransformer(MONEY_COLUMNS),
ChoiceColumnsTransformer(TRAIN_COLUMNS_WITH_DAYS),
VCTransformer(VC_COLUMNS),FillNATransformer(['days'],na_val=0),
ExtraTreesClassifier(random_state=SEED, min_samples_leaf=86))
stk_estimators=[
#('lgb', lgb_mod),
#('rf', rf_mod),
('cbt_s', copy.deepcopy(cbt_mod)),
('lda_s', copy.deepcopy(lda_mod)),
('lgb_s', copy.deepcopy(lgb_mod)),
('ext_s', copy.deepcopy(ext_mod)),
('lr_s', copy.deepcopy(lr_mod)),
]
stk_fin_estimator=CalibratedClassifierCV(LinearSVC(random_state=SEED,max_iter=5000))
estimators = [
#('vot',VotingClassifier(vot_estimators,voting='soft')),
('stk',StackingClassifier(stk_estimators,stack_method='predict_proba',final_estimator=stk_fin_estimator)),
]
2.3. モデルの学習と検証
2.3.1. Cross Validation
RepeatedStratifiedKFold(n_repeats=4, n_splits=12, random_state=SEED)
2.3.2. 評価:AUC
2.3.3. 評価:F1(average='macro)
predict_probaの算出値に対してthresholdを0.5~0.85までずらしながら結果を評価
f1_score(y_true, y_pred >= threshold, average='macro')
2.3. submission.csvの作成
検証データでF1スコアが0.68以上のモデルのみを選定しvoting ensemble
pred = pd.DataFrame()
for i, (score,[iter, name, model]) in enumerate(zip(df_score['f1_0.82'],models)):
if score > 0.68:
pred[i] = model.predict_proba(test_prepared)[:,1]
pred_final = (pred[pred.columns].apply(np.mean,axis=1) > 0.82).astype(int)
pred_final.value_counts()/len(pred_final)
2.4. ソース
3. まとめ
databricksという非常に強力な開発環境のおかげで色々なモデルを検証できました
結果的に49位という望外な結果に大満足しています
事務局ならびにdatabricks関係者にお礼申し上げます