電通総研 テックブログ

電通総研が運営する技術ブログ

XGBoostでオンラインゲームの課金ユーザーを予測する

こんにちは。コミュニケーションIT事業部 ITソリューション部の英です。

普段はWebアプリやスマホアプリの案件などを担当しています。あと、趣味で AI を勉強しています。

XGBoostは機械学習で非常に人気のあるアルゴリズムの一つで、特に表データにおける予測問題で高い性能を発揮します。
"非ディープラーニングの手法"の中ではとても高い性能を発揮するとされています。用途としては売上予測や顧客の離脱予測など、分類問題や回帰問題に用いられます。

なんだか難しい言葉がいっぱい出てきました。

今回は初歩から丁寧に解説していこうと思います。まずXGBoostは「eXtreme Gradient Boosting」の略です。
"エクストリーム勾配決定木"、かっこいいですね。

そもそも決定木とは?

データを分類を行うためのシンプルで強力な機械学習の手法です。質問を繰り返し選択することで答えを導く「木構造」のモデルです。入力値を根から投入すると、内部ノード(if文)をたくさんくぐって最終的に1つの葉に落ちます。

アプリに課金するかどうかの決定木(イメージ)

学習データ作成

葉の部分を良くみると、最後の分岐で必ず「Yes/No」の2択に落ちていますよね。
古いレコメンドシステムだとこのようにif文をベタ打ちだったりしますよね。

ランダムフォレストとはなにか?

先ほどの決定木では過学習(Overfitting)に陥ることがあり、モデルの汎用性が低い状態です。過学習とはトレーニングデータに過剰にフィットしてしまい、検証データで良い性能が出ない状態のことです。

そこで登場したのがランダムフォレストです。複数の決定木を同時に作成し、全ての木の出力結果をもとに最終的な出力が決まります。
このように、複数の学習モデル(分類器や回帰器)を組み合わせて、単一のモデルよりも優れた予測性能を得る機械学習の手法をアンサンブル学習と言います。これにより予測の安定性が増し、過学習のリスクを抑えることが期待できます。

以下は同じテーマですが、木の構造が先ほどと異なります。このように、最終的なYes / Noへのルートや分岐が木ごとに異なるわけです。分類モデルの場合、最終的な結論(Yes / No)はすべての木が出力する予測結果の多数決で決まります。
学習データ作成

勾配決定木とは何か?

先ほどの木を見ていただきたいのですが、「フレンド数 ≦ 3」がNoって少し"適当"だと思いませんか?
このように、各Yes / Noは同じ価値ではないわけです。そこで各葉にスコアリングし、決定木が前の決定木の残差(誤差)を逐次的に学習することで、モデルの予測性能を段階的に向上させます。

勾配決定木は、以下の手順で構築されます。

  1. 初期モデル: 最初に簡単なモデルを作成する
  2. 残差の計算: 予測値(確率)と正解ラベル(1 / 0)の差(残差)を計算する。
  3. 新しい木を作成: 残差を減らすように新しい決定木を作成する
  4. モデルの更新: 新しい決定木をモデルに追加する
  5. 繰り返し: このプロセスを繰り返して、モデルを改善する

XGBoostとは何か?

勾配ブースティングの手法を高度に最適化した実装であり、機械学習コンペティションや実用的なデータ分析で広く使われている非常に人気のあるライブラリです。そう、ライブラリです。モデルと言っても良いです。

ここから本題

今回はSageMakerのXGBoostを使って分類モデルの学習を行い、k分割交差検証を使って混合行列でモデルの性能を評価してみます。
オンラインゲームのユーザーがゲームに課金するかどうか」を予測させるモデルを作ります。ゲームのプレイ時間や実績の達成状況、年齢などを学習データとして与えてみます。検証をシンプルにするために特徴量は少なめにします。

STEP1:学習データの準備

Pythonスクリプトで学習用データのCSVを一括で作成します。10000レコード作成しましょう。
ゲーム時間(hours_played)と実績数(achievement_count)は正規分布を使って割合を調整しています。これはゲームにハマっているユーザーと、そうでないユーザーの傾向を表現するためです。完全なランダムで生成すると予測精度に影響が出てしまいます。ゲーム時間と実績数を主成分だと仮定して、これらが多ければpaid(正解ラベル)が1を取りやすくします。係数は適宜調整をしてください。
課金する確率を操作しているだけであるため、ゲームのプレイ時間が長く、実績数が多いユーザーでも無課金ユーザーになる可能性があるということです。これは現実でもそうですね。
年齢(age)とフレンド数(friends_count)は課金確率に影響を与えない仕組みになっているため今回はノイズ要素になります。
学習データ作成

生成されたCSVは以下のようなかたちです。
今回は全体の約38%のユーザーが課金したと仮定したデータになりました。
今回使用するXGBoostは一般的に正規化や正則化が不要なモデルのため、前処理を施さずにこのまま学習データとして使用します。

列名 説明
age ユーザーの年齢
hours_played プレイ時間 ※paidの確率に影響
friends_count フレンド数
achievement_count 実績数 ※paidの確率に影響
paid 目的変数 ※1が課金ユーザー、0が無課金ユーザー

CSV

STEP2:各種ライブラリのインポート

以下の通り、各種ライブラリをインポートします。
※下線は未使用警告なので無視していただいて構いません
ライブラリのインポート

STEP3:データの読み込みと準備

paidは"課金したユーザーかどうか"を示す正解ラベルですから、yにセットします。それ以外を特徴量としてxにセットします。
データ読み込み

STEP4:k分割交差検証の準備

今回は全データを5つのサブセットに分割します。サブセット4つで学習し、残り1つで評価することを5回繰り返します。shuffle(True)にすることで、データの並び順によって生じるデータの偏り、それによる学習結果への影響を抑えることができます。random_stateはシャッフル時の乱数生成のシード値を設定するものです。再現性のために指定しています。
k分割交差検証

STEP5:検証

以下の通り繰り返し検証を行い、そのたびに推論エンドポイントをdeleteします。推論エンドポイントは放置すると課金されてしまうので、必ず削除するようにしてください。
今回のデータでは課金ユーザーが少数派であり学習結果に影響を与える可能性があるため、SMOTE(Synthetic Minority Over-sampling Technique)を使用してオーバーサンプリングしています。SMOTEは少数派のサンプルとその近傍のサンプルを組み合わせて新しいサンプルを作成し、データのバランスを改善します。
トレーニング

レーニングが開始されました。

結果を待っている間に少しハイパーパラメーターについて解説します。
RandomizedSearchCVを用いることで、最適なハイパーパラメーターを見つけながら、最適なモデルを構築します。

パラメータ 説明
max_depth 各決定木の最大の深さです。モデルの複雑さを制御します。
eta 学習率です。各学習ステップでの重みの更新量を調整して学習の安定性を向上させます。
gamma 分割が必要かどうかを決めるためのノード分割コストに対する閾値です。予測の正確性が少なくとも設定値以上改善されない限り、新しい分岐は作りません。
min_child_weight 子ノードが持つべき最小のサンプル数の合計重みです。過学習を防ぐために小さなサンプルの分割を抑制します。
subsample 各決定木を構築する際に使用するランダムに選択されたサンプルの割合です。
objective='binary:logistic' バイナリ分類のためのロジスティック回帰を目的関数として設定し、確率的な出力を生成します。
num_round 構築する決定木の数です。

STEP6:評価

confusion_matrixの結果は以下の並びで表示されます。TP(True Positive)が左上にくるイメージがありますよね。TN(True Negative)が左上に来るので注意してください。

Actual / Predicted Negative Positive
Negative TN FP
Positive FN TP

学習と検証が完了したので評価を表示してみます。

一番性能が良さそうなFold5をピックアップして中身を読み解きましょう。

  • 全体的な精度(accuracy)は70% であり、比較的良好な結果と言えます。
  • クラス0の適合率が82%で、これはモデルがクラス0を予測する際に正確であることを示しています。
  • クラス1の適合率が58%と低めであり、これはクラス1を予測する際に誤った予測が多いことを示しています。
  • クラス1の再現率が77%で、これはモデルがクラス1を見逃す確率が低いことを示しています。
  • クラス0の再現率が65%と低めであり、クラス0を見逃すケースが一定数存在することを示しています。

全体的に良好な結果ですが、クラス1の適合率が低いことが分かります。
なぜ、クラス1の適合率が低いのかはテストデータを見れば明らかです。今回は学習用データの作成の際に"ゲームに課金する確率"を操作して、"ゲームに課金する素質がある人"でも課金する(paid=1)確率は約4割、課金しない(paid=0)確率が約6割存在するためです。このような不確実性を持ったデータに対する学習にしては、モデルの品質はかなり良い結果であると考えられます。

機械学習の分類手法は他にもたくさんあるので、気が向いたら検証してみようと思います。
最後まで読んでいただき、ありがとうございました。

執筆:英 良治 (@hanabusa.ryoji)、レビュー:@yamada.y
Shodoで執筆されました