クレジットカードの支払い履行・不履行の予測

β版ProbSpaceコンペ第1弾!

賞金: 100,000 参加ユーザー数: 376 5年弱前に終了

ここからはじめよう

データサイエンスや機械学習の知識を活かして、コンペティションに挑戦してみよう!

コンペの説明

クレジットカード会社にとって、支払い不履行になり得る顧客を予測することは、最も重要なタスクの一つです。

このコンペティションでは、default of credit card clientsと呼ばれるデータセットを用いて、顧客の支払いの履行・不履行の予測に挑戦します。
このデータは台湾における顧客のクレジットカード支払いに関するデータです。

賞金

1位 100,000円
※ 対象者には、コンペティション終了後 メールにてご連絡いたします。

必要なスキル

  • Pythonの基本
  • 機械学習による2値分類

参考文献

実践Data Scienceシリーズ PythonではじめるKaggleスタートブック (KS情報科学専門書)
Pythonで動かして学ぶ! Kaggleデータ分析入門 (AI & TECHNOLOGY)

ダウンロード

データをダウンロードするにはログインまたはユーザー登録して下さい

概要

データは2つのグループに分けられます:

  • 訓練データセット(train_data.csv)
  • テストデータセット(test_data.csv)

なお訓練データセットとテストデータセットにはそれぞれ27,000と3,000のサンプルが含まれています。
訓練データセットは、機械学習モデルを構築するために使用してください。訓練データセットについては、不履行か履行かのラベルが振られています。
テストデータセットを使用して、どの程度新しいデータに対してモデルが実行されているかを確認する必要があるので、テストデータセットについては、
顧客ごとのラベルデータは存在しません。
なので、テストデータセットを使用して、各顧客のラベルを予測してください。

また、提出ファイルの例に関しては 評価方法 のタグを参照してください。

データの形式

データセットのカラムは以下の通りになっています。

カラム名 内容
X1 与えられたクレジットの金額(NTドル)個人消費者クレジットと家族(補足)クレジットの両方を含みます
X2 性別(1=男性, 2=女性)
X3 教育(1=大学院, 2=大学, 3=高校, 4=その他)
X4 婚姻状況(1=既婚, 2=単身, 3=その他)
X5 年齢(年)
X6 過去の毎月の支払記録(2005年9月の返済状況)
X7 過去の毎月の支払記録(2005年8月の返済状況)
X8 過去の毎月の支払記録(2005年7月の返済状況)
X9 過去の毎月の支払記録(2005年6月の返済状況)
X10 過去の毎月の支払記録(2005年5月の返済状況)
X11 過去の毎月の支払記録(2005年4月の返済状況)
X12 請求書の金額(NTドル)/2005年9月の請求書明細書
X13 請求書の金額(NTドル)/2005年8月の請求書明細書
X14 請求書の金額(NTドル)/2005年7月の請求書明細書
X15 請求書の金額(NTドル)/2005年6月の請求書明細書
X16 請求書の金額(NTドル)/2005年5月の請求書明細書
X17 請求書の金額(NTドル)/2005年4月の請求書明細書
X18 前払い金額(NTドル)/2005年9月に支払われた金額
X19 前払い金額(NTドル)/2005年8月に支払われた金額
X20 前払い金額(NTドル)/2005年7月に支払われた金額
X21 前払い金額(NTドル)/2005年6月に支払われた金額
X22 前払い金額(NTドル)/2005年5月に支払われた金額
X23 前払い金額(NTドル)/2005年4月に支払われた金額

※ X6-X11の測定スケールは、-1=正当な支払い, 1=支払い遅延1か月間, 2=2ヶ月間の支払い遅延, ..., 8=8ヶ月間の支払い遅延, 9=9ヶ月以上の支払い遅延 となっています。

ラベル'Y'は以下の通りです

ラベル 説明
0 支払い履行
1 支払い不履行

目標

目標は、テストデータセットのクレジットカード使用者のデータに基づき、支払い不履行か、履行かを予測することです。それぞれの顧客IDにおいて、変数Y(支払い
不履行 or 履行)を1 or 0で予測してください。

メトリック

モデルの予測性能は3,000のテストデータセットのうち、予測が正解した割合で与えられます。

$$
\text{accuracy} = \frac{\text{correct prediction}}{\text{total}}
$$

提出ファイルの形式

回答用のsubmission.csvを用意する(3,000のエントリーとヘッダー行を含む) 。
提出されたファイルに余分な行や列(IDとY以外)が含まれていた場合はエラーとなります。

提出ファイルは以下の列のみを必ず含んでください:

  • ID(テストデータセットと同じ順序)
  • Y(支払い不履行かどうかの1, 0)

以下は提出ファイルの例です。

ID,Y
0,1
1,1
2,0
3,1
4,0

参加者ごとに1つのアカウント

複数のアカウントを使ってコンペティションに参加することはできません。

私的な情報共有の禁止

コードやデータを,特定のコンペティション参加者に個人的に共有することはできません。コンペティションの全ての参加者が利用できるような状態であれば、コードを共有することは問題ありません。

送信制限

1日あたり最大5件の応募が可能です。
審査のために最終提出を5件まで選択することができます。

コンペティションのタイムライン

開始日 2019/4/9 0:00 JST
エントリー締め切り なし
終了日 2019/6/3 0:00 JST

賞金の支払い

優勝者は、以下の条件を満たすことで賞金を受け取ることができます。

  1. モデルの提出
    最終成果物であるcsvの作成に使用した、学習済みモデルをご提出いただきます。提出後、弊社側で不正がないことを確認します。
    ※不正が疑われる場合は、コンペティション開催中であってもモデル提出を依頼する場合があります
  2. モデルの説明
    ディスカッション&ナレッジにて、学習済みモデルの解説をお願いします。
    また解説では、学習済みモデルのソースコード(ファイル)を添付するか、ソースコードへのリンク(URL)を貼り付けてください。

外部データの使用は禁止

本コンペティションで公開されているデータのみを用いてチャレンジして下さい。また、学習済みモデルを用いることも外部データを用いているとみなします(FAQより転載)。

運営からのお願い

公平性の担保、チーティング等の不正防止のため、β版のコンペでは、予告なくルールの追加・変更を行う場合がございます。
ご不便をおかけすることもあるかと思いますが、サービス向上のためご了承ください。

このコンペティションでは賞金はでますか?

はい。最も精度の高い学習モデルを作成した優勝者には、賞金10万円を贈呈します。ただし、賞金を受け取るにあたっては、学習済みモデルの提出、解説の作成が必要となります。

チームで参加できますか?

可能です。現時点ではチームとして参加する機能は未実装のため、参加者ごとに同じ結果を提出いただいて構いません。
※ 同率スコアの場合は提出時間の早いユーザーが上位となりますこと、ご了承ください。

外部データを使うことは可能ですか?

本コンペティションで公開されているデータのみを用いてチャレンジして下さい。また、学習済みモデルを用いることも外部データを用いているとみなします。

どこでアカウントをつくればいいですか?

[こちら](https://prob.space/users/sign_up)から作っていただけます。
アカウントを作らないと、コンペティションに参加することはできないので気をつけてください。

コードを提出するにあたって Seed を固定する必要はありますか?

Seed を固定することが推奨です.ただし,Seed を固定しなくても提出用コードとしては認めていく方針です。

データの読み込み

まずはデータを読み込んで見ましょう.csvデータの読み込みは複数のやり方がありえますが,pandasread_csv関数はその中でも機能が豊富で,扱いやすいためこれを使います.
これを使うと,csvデータを読み込み,pandas dataframeにして返してくれます.

また,dataframeには.head()というメソッドが定義されており,これを使うと先頭の数行を確認できます.

import pandas as pd

path = "./data/train_data.csv"
df = pd.read_csv(path)

# >>> df.head()
#    ID      X1  X2  X3  X4  X5  X6 ...   X18    X19    X20   X21   X22   X23  Y
# 0   0   20000   2   2   1  24   2 ...     0    689      0     0     0     0  1
# 1   1  120000   2   2   2  26  -1 ...     0   1000   1000  1000     0  2000  1
# 2   2   90000   2   2   2  34   0 ...  1518   1500   1000  1000  1000  5000  0
# 3   3   50000   1   2   1  57  -1 ...  2000  36681  10000  9000   689   679  0
# 4   4   50000   1   1   2  37   0 ...  2500   1815    657  1000  1000   800  0

最適化の際に収束性を保証するため,入力データに平均が0で分散が1であるという仮定を置くものがままあります.そこで,連続値のデータに対しては平均が0で分散が1になるように正規化しておきます.(標準化と呼ばれるケースもあります.)
ここでは,離散的な変数(性別や教育などのダミー変数)については除外し,正規化を行います.

from sklearn.preprocessing import StandardScaler

def standardize(df: pd.DataFrame) -> pd.DataFrame:
    """
    Parameters
    ----------
    df : pd.DataFrame

    Returns
    -------
    _df : pd.DataFrame
    """
    _df = df.copy()
    _df_subset = _df.iloc[:, 12:]

    if 'Y' in _df_subset.columns:
        _df_subset = _df_subset.drop('Y', axis=1)

    scaler = StandardScaler()
    X_subset_std = scaler.fit_transform(_df_subset)

    _df.loc[:, _df_subset.columns] = X_subset_std

    return _df

def load_train_data(path: str, standardized: bool=True) -> np.ndarray:
    """
    Parameters
    ----------
    path : str
        path of train_data.csv
    standardized : bool
        If True, train_data.csv will be standardized.

    Returns
    -------
    X : np.ndarray
    y : np.ndarray

    Examples
    --------
    >>> path = "./data/train_data.csv"
    >>> X, y = load_train_data(path, standardized=True)
    """
    df = pd.read_csv(path)

    if standardized:
        df = standardize(df)

    X = df.iloc[:, 1:-1].values
    y = df.Y.values

    return X, y

ここで定義した関数を用いると,以下のようにしてcsvデータを読み込むことができます.

path = "./data/train_data.csv"
X, y = load_train_data(path, standardized=True)

データの確認

訓練データにおいて,そもそも支払い履行と支払い不履行がどのような割合なのか確認してみましょう.

one_ratio = y.sum() / len(y)
print("ratio of 1 in label: ", one_ratio)
print("ratio of 0 in label: ", 1 - one_ratio)
# ratio of 1 in label:  0.222
# ratio of 0 in label:  0.778

支払い不履行が22%ほど含まれたデータであることがわかります.

pandas dataframeのメソッドには,describeメソッドという便利なものがあります.
これを使うと,dataframeの各列について,基本的な統計量を確認することができます.

path = "./data/train_data.csv"
df = pd.read_csv(path)
df.describe()
#                 ID              X1      ...                 X23             Y
# count  27000.00000    27000.000000      ...        27000.000000  27000.000000
# mean   13499.50000   166856.284444      ...         5238.613148      0.222000
# std     7794.37297   129363.239705      ...        17865.467132      0.415599
# min        0.00000    10000.000000      ...            0.000000      0.000000
# 25%     6749.75000    50000.000000      ...          140.000000      0.000000
# 50%    13499.50000   140000.000000      ...         1500.000000      0.000000
# 75%    20249.25000   240000.000000      ...         4002.250000      0.000000
# max    26999.00000  1000000.000000      ...       528666.000000      1.000000

# [8 rows x 25 columns]

識別モデルの作成

ここでは試しに,ロジスティック回帰モデルを作成してみましょう.
sklearnは多くの機械学習モデルが実装されたライブラリであり,これを使って実装してみます.

from sklearn.linear_model import LogisticRegression

path = "./data/train_data.csv"
X, y = load_train_data(path, standardized=True)
lr = LogisticRegression(tol=1e-6, class_weight={0: 0.4, 1: 0.6})
lr = lr.fit(X, y)

先ほどデータを確認した際に,支払い不履行の割合が22%でした.このように,ラベルごとにデータの割合がアンバランスであるようなデータを,不均衡データと言います.このようなデータを用いてモデルの学習を行うと,全て0と答えるモデルができてしまうケースがあります.(今回の場合,全て0と答えるだけで78%の正答率が得られています.しかし,これは我々が望んでいるモデルではありません.)
それを回避するため,class_weightオプションで,1を間違いなく識別することを,0を間違いなく識別することよりも重要視するように設定しています.

モデルの評価

sklearnの識別モデルには,.predict()メソッドが定義されており,これを使うと与えられたデータに対して,その0か1かの予測結果を返してくれます.

また,予測結果と実際の結果がどの程度異なるかを評価する関数がsklearn.metricsに定義されています.ここでは精度を確認してみましょう.

from sklearn.metrics import accuracy_score

y_hat = lr.predict(X)
print(accuracy_score(y, y_hat))
# 0.8162962962962963

このように,81.6%程度となり,全て0と識別するモデルに比べて4%ほど精度が増加しています.

これをベースにして,モデルのパラメータを変える,モデルを変える,有用な特徴量を探るなど試行錯誤して,モデルの性能を上げていきましょう.