アプリとサービスのすすめ

アプリやIT系のサービスを中心に書いていきます。たまに副業やビジネス関係の情報なども気ままにつづります

xgboostの回帰モデルで精度検証から重要な特徴量選択までやってみた全行程まとめ(機械学習)

今回はkaggleでよくある特徴量エンジニアリングのテクを使って、精度向上から重要な特徴選択までをやった。普通は精度高ければ終わり的な感じだけど、今回は精度検証からさらに掘り下げて、特徴量の選択までやったので、その過程を書いてく。

目次
・プロジェクト紹介
・デーセット
・特徴量作成
・モデル構築
・正解率
・特徴量の探索(分析)
・最後に(反省点)

コードはこちら




プロジェクト紹介


一回しか購入しないユーザー(u1)と2回以上購入しているユーザー(u2)を分ける重要な特徴量を見つけるプロジェクト。目的は一回しか買わないユーザーに有効な施策をうつこと。

よくある特徴量エンジニアリングで、回帰モデルを使って精度を競うkaggleとかのコンペと似ている。けど、今回は精度検証後に重要な特徴量選択までしたので、特徴量選択の分析過程も含まれてる。全体の工程としては、特徴量作成が7、8割を占めた感じ。

特徴量エンジニアリングが、精度向上に重要な過程だと再認識。





データセット


データセットはデータベースに保存してあるテーブルのレコード(特徴量)から選択して作成。kaggleみたいにあらかじめ綺麗なのが用意されてないし、データベースとbigqueryに別々に保存してある。そんな中、無数にある特徴量から適切なのをSQLで取り出して、ベストな特徴量を作成。

最終的な特徴量は、2回目の検証結果から決めた。アルゴリズムでの検証は全体で3回。

まず1回目は商品に結びつきそうな特徴量を選んで検証。1回目はtouroku_dateという特徴量が一番重要らしく、次の特徴量を選ぶ指針になった。

2回目は施策から「u1が1回で購入をやめてしまう」ことに関する仮説を立てて、この仮説に直結する特徴を選んで使うことにした。
仮説の設定はAmazonとか、リピート率が鬼のように高いサイトレビューとかを参考にした。


一回で購入をやめてしまう or 継続して購入する理由の仮説

=> 評価の悪いユーザーから購入してる
=> レコメンド関係の施策で欲しい商品を購入してる
=> 欲しい商品が見つからない(時間をかけて探していない)



3回目は特徴量を2個追加して、若干の修正後に再検証。






***仮説を立てることで作業を効率化
無数にある特徴量の中から、適切なものを選択する過程で、施策に応じて仮説を立てる作業はかなりの効率化につながった。はじめに仮説を立てるのは、データサイエンティスト的な手法らしい。

さらに、選んだ特徴量から、u1とu2間の違いをあらかじめ調べる作業をする事で、さらに効率化。違いを見るのには「標準偏差・平均・中央値」とかの分析手法を使った。

特徴量は何を選べばいいかわからない状況下で、

・仮説の設定

・違いを見る作業

この二つをすることで、かなり効率化できた。

特徴量に必要な特徴量を選択するまでの、全行程はこんな感じ

1、まず分類に使えそうな特徴量を作成・検証

2、試作にあった仮説を立てる(重要)

3、仮説に直結する特徴量を選んで、標準偏差とかで違いをみる(重要)

4、特徴量を決めて、作成

2、3の作業が一番精度向上につながった。

最終的なデータセットは、
特徴量10こ
u1は250853件、u2は1169898件のデータを使った。




特徴量作成


特徴量の作成過程は上で書いた通りなので、ここではデータベースのレコードをSQLで取り出し、それを使いやすい形に変えて特徴量を作成する特徴量エンジニアリングのテクニックについて触れたい。

特徴量エンジニアリングでは主に質的変数(赤、青、白とかみたいな数字じゃない形)をダミー変数に、量的変数(体重とか身長みたいな数値)を使いやすく変形させるテクがある。

特にダミー変数は離散化とか、いろいろあるので、ここら辺の資料を参考にした。

機械学習レシピ#3

データの前処理だけで競馬は強くなる

特徴量の形を使いやすい形に変えるのも、めっちゃ大事。
今回は可視化した結果の分布が、「0が多く、0以上が少ない」場合、SQLでこんな感じに取り出すのが有効だった。

SUM(case when レコード= 値 then 1 else 0 end) 


逆に分布がバラバラな特徴量は、AVG() で平均をとるか、そのまま使うのが有効だった。

特徴量は検証前に、標準化しないと分類精度がすごい下がるので、データ作成後は必ず標準化した。






モデル構築


モデルは、kaggleで有名なランダムファレストの改良版のxgboostを使った。
特にGBDTの改良手法Dropouts meet Multiple Addtive Regression Trees(DART)とパラメーターチューニングのhyperoptの組み合わせが、xgboostとドンピシャで、かなりいい分析精度になった。

xgboostのパラメータは山ほどあるので、チューニングはベストプラクティスを載せてるサイトがを参考にした。

Parameter_Tuning_XGBoost_with_Example

パラメータチューニングはhyperoptとgridseachを使い分けて、手早くチューニング。

精度検証で、交差検証はもはや当たり前で、さらに複数のアルゴリズムでの検証や、複数の精度指標での試行錯誤も王道らしい。

def objective(params):

    skf = cross_validation.StratifiedKFold(
        train_y, # Samples to split in K folds
        n_folds=5, # Number of folds. Must be at least 2.
        shuffle=True, # Whether to shuffle each stratification of the data before splitting into batches.
        random_state=30 # pseudo-random number generator state used for shuffling
    )

    boost_rounds = []
    score = []

    for train, test in skf:
        _train_x, _test_x, _train_y, _test_y = \
            train_x[train], train_x[test], train_y[train], train_y[test]

        train_xd = xgb.DMatrix(_train_x, label=_train_y)
        test_xd = xgb.DMatrix(_test_x, label=_test_y)
        watchlist = [(train_xd, 'train'),(test_xd, 'eval')]

        model = xgb.train(
            params,
            train_xd,
            num_boost_round=100,
            evals=watchlist,
            early_stopping_rounds=30
        )

        boost_rounds.append(model.best_iteration)
        score.append(model.best_score)

    print('average of best iteration:', np.average(boost_rounds))
    return {'loss': np.average(score), 'status': STATUS_OK}

def optimize(trials):
    space = {'booster':'dart',
         'learning_rate':0.1,
         'n_estimators':1000,
         'sample_type':'uniform',
         'normalize_type': 'tree',
         'objective':'binary:logistic',
         'min_child_weight':1,
         'max_depth':9,
         'gamma':0.0,
         'subsample':0.6,
         'colsample_bytree':0.9,
         'reg_alpha':1e-05,
         'nthread':4,
         'scale_pos_weight':1,
         'seed':27,}
    
    # minimize the objective over the space
    best_params = fmin(
        fn=objective,
        space=space,
        algo=tpe.suggest,
        trials=trials,
        max_evals=10
    )

    return best_params

# parameter tuning
trials = Trials()
best_params = optimize(trials)
print(best_params)

# 損失関数(loss)の計算
print(objective(best_params))






正解率


正解率は


AUCで89.33%



MCCで0.548(-1~1の範囲をとり、1で100%の精度)



特徴量の重要度


評価指標はAUCがよく使われるけど、不均衡データとかより正確な精度判定にはMCCの方がいいっぽい。

from sklearn.metrics import matthews_corrcoef
thresholds = np.linspace(0.01, 0.99, 50)
mcc = np.array([matthews_corrcoef(test_y, pred_y>thr) for thr in thresholds])
plt.plot(thresholds, mcc)
best_threshold = thresholds[mcc.argmax()]
print(mcc.max())

この他にも混合行列とかF値を使ってもいいけど、今回はMCCがF値と同じくらい精密なので、MCCで代用。




特徴量の探索(分析)



特徴量の重要度、そのツリー、xgboostの結果から、u1とu2に有効な特徴量選択。検証からさらに特徴量を探すまで深掘りするケースはサイトでは見つからなかったので、このフェーズが一番大変だった。
とりあえず、一般的な統計手法とか、マーケット分析事例で使われてるっぽい分析手法を漁った。

あとは知識、ひらめき、そしてひたすら根気の作業。
結果、ヒストグラムクラスタリング、次元削減、重回帰分析とかが使えるっぽくて、一番合理的な「重回帰分析」を使った。

重回帰分析は、ある一つの特徴量と、他の複数の特徴量の相関関係を計算してくれる

結果的に、

u1=>海外商品系の特徴量

u2=>満足度が高い特徴量

との相関が高く、これらが重要な特徴量と判断。

結論とその理由とかのロジック的な部分は、他の特徴量との相関関係の数値、仮説の消去法で導いた感じ。ちゃんと根拠つきで




最後に(反省点)



kaggleとかの特徴量エンジニアリングを使った分析は、あらかじめ綺麗な特徴量が用意されていて、分析精度が良ければ終わり、みたいなケースが多い。
けど今回はさらに掘り下げて、重要な特徴量の選択までやった。前例がないとほぼ手探りなので、かなり大変だった。

とりあえず、反省点として

・試行錯誤より頭を使って、作業量を減らす(think more, do less)

・行き詰まったり、停滞したら、一回止まって問題の分解とか本質の問題探しをする。絶対にただタスクをこなすだけのループにはまらないこと
(今回はマッキンゼーの問題解決法の書籍が本質の問題探しにかなり使えた
イシューからはじめよ―知的生産の「シンプルな本質」
世界一やさしい問題解決の授業―自分で考え、行動する力が身につく


・チームで動く以上、進歩確認は随時やる


・データ量が多ければ、少ないデータ量(1万件くらい)で試す


・やることの意思疎通にすれ違いがないようにして、やり直しは極力やらない


・段取り8割


・わからないことは、詳しい人に積極的に、遠慮なく聞くこと

あえてxgboostのDARTみたいなハイリスク・ハイリターンのスキルを使うことで、かなりのスキルが身についたなと実感した。




kaggleのテクニック系の参考資料
top2%の私が教えるKaggleの極意

Kaggleで使われた特徴量エンジニアリングとアルゴリズムまとめ