こんにちは、最近はエンジニアよりデータサイエンティストとしての仕事が多くなってきたt_sakaiです。
入社後4ヶ月、ようやくアウトプットを出せるようになってきました。
本記事について
先日Treasure Data Tech Talkという勉強会に登壇して、機械学習を用いてリアルタイムCTR予測を行う発表をしてきました。
その時に用いた資料がこちらです。
この資料では、以下のような内容が説明されています。
- 機械学習によるCTR予測
- nex8でのリアルタイムCTR予測システムの構成
- ScalaからTreasureDataを使う際のノウハウ
上記スライドは機械学習の知識がない人にも分かるように作ったので、本ブログ記事ではもう少し踏みこんで、作成した予測モデルの性能評価方法について説明しようと思います。
予測モデルの性能評価
上記スライドでは予測モデルの作り方と、それを用いて実際にCTR予測を行う方法を説明しました。 しかしその予測が本当に正しいのかどうかを検証しなくては、本番環境で使うわけにはいきません。
前提ですが、「予測モデルのAの性能の良さ」 = 「予測モデルAを使って予測した結果の精度の良さ」です。 ここからは「予測した結果の精度の良さ」を考えていきます。
予測結果データの用意
学習用データの一部をテストデータとして分け、それに対して実際に予測をしてみます。 すると以下のようなデータが得られます。
この結果例を見たときに考えられることは以下のような感じでしょうか。
- ◯ 予測CTRが高い3番目のデータがクリックされているぞ
- ✖ 実際の平均CTRは25%なのに、平均予測CTRは1%もないぞ
予測結果データは実際には何億という件数になるので、1件1件人の目で見ていくことはできません。 そこで、数学的な根拠に基づいたいくつかの精度指標を紹介します。
ROC曲線とAUC値
この指標はYahoo!のCTR予測の論文で使われています。
ROC曲線
ROC曲線は真陽性と偽陽性の割合をもとに描かれた以下の図のような曲線です。
もし全くランダムにCTRを予測した場合は、左下から右上へのまっすぐな点線がROC曲線と一致します。
ROC曲線が左上に偏っているほど、その予測モデルの性能は良いと言えます。
AUC値
AUC値は、ROC曲線の右下の部分の面積です。
全くランダムに予測した場合のAUC値は0.5、完璧に予測できた場合のAUC値は1です。 なのでAUC値が1に近いほど、モデルの精度は良いと言えます。
Python3コード
fpr, tpr, thresholds = roc_curve(act, pred) print(auc(fpr, tpr)) # AUC値 # ROC曲線 plt.clf() plt.plot(fpr, tpr, label='ROC curve (area = %0.2f)' % roc_auc) plt.plot([0, 1], [0, 1], 'k--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.0]) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('ROC curve') plt.legend(loc="lower right") plt.show()
ROC曲線の問題点
ROC曲線はとても良い指標なのですが、実はCTR予測モデルの性能評価には向かない点があります。 そもそもROC曲線は二値分類器の性能を測るためのもので、CTR予測のような回帰モデルを評価するためのものではありません。
そのため、「モデルのキャリブレーション」が考慮されないという大きな問題点があります。 モデルのキャリブレーションとは、予測されたCTRが実際のCTRとどれだけ一致しているか、ということです。
この問題のため、予測されたCTRを全部10倍にしたとしても、ROC曲線やAUC値は一切変わらないのです。 言い換えれば、ROC曲線は「クリックされそうなやつとクリックされなさそうなやつを分ける性能」は測れても、「実際にクリックされる確率を正確に予測する性能」は測れないということになります。
Logarithmic Loss
モデルのキャリブレーションも考慮した指標です。 Criteoがkaggle上で行ったCTR予測コンペの精度評価もこの指標が用いられていました。
この値が小さいほど実際のCTRとのズレが少なかったということになります。
Python3コード
import scipy as sp def logloss(act, pred): epsilon = 1e-15 pred = sp.maximum(epsilon, pred) pred = sp.minimum(1-epsilon, pred) ll = sum(act*sp.log(pred) + sp.subtract(1,act)*sp.log(sp.subtract(1,pred))) ll = ll * -1.0/len(act) return ll
Logarithmic Lossの問題点
Logarithmic Lossはテストデータの平均CTRによって、値が大きく変わってしまうという問題があります。 そのため、この指標を用いて複数のモデルを評価する際は、同一のテストデータを用いるように注意してください。
以下に、平均CTRとLogarithmic Lossの最小値のグラフを提示しておきます。
これを見ると分かるように、平均CTRが0.5に近づくほどLogarithmic Lossの最小値は大きくなります。
import scipy as sp import matplotlib.pyplot as plt def logloss_x(x): # 平均CTRが x のときのLogarithmic Lossの最小値を返す。 return -1 * (x * sp.log(x) + (1-x) * sp.log(1-x)) x = np.arange(0.01, 1.0, 0.01) y = np.vectorize(logloss_x)(x) plt.clf() plt.plot(x, y) plt.xlabel('Mean CTR') plt.ylabel('Minimum logarithmic loss') plt.show()
まとめ
ROC曲線とAUC値
- ◯ テストデータの違いの影響を受けにくい
- ✖ モデルのキャリブレーションが評価されない
Logarithmic Loss
- ◯ モデルのキャリブレーションが評価される
- ✖ テストデータによって値が大きく異なる
さいごに
ファンコミュニケーションズでは、大規模データを扱えるエンジニアを募集しています。 ご興味のある方は是非ご応募よろしくお願いいたします。 www.fancs.com