Np-Urのデータ分析教室

オーブンソースデータなどWeb上から入手できるデータを用いて、RとPython両方使って分析した結果を書いていきます

【ROC曲線とAUC】機械学習の評価指標についての基礎講座

機械学習ではモデルを作って終わり、ということは無く、モデル作成後にテストデータを使って「本当に良いモデルなのか?」という評価を必ず行う必要があります。

では具体的にどのように評価をすれば良いのか?という話になりますが、今回は代表的な評価指標である

  • ROC
  • AUC

ついて説明していきます。

この辺りについては、以下書籍でよくまとまっているので、よろしければ是非!

Pythonと実データで遊んで学ぶ データ分析講座

Pythonと実データで遊んで学ぶ データ分析講座

  • 作者: 梅津雄一,中野貴広
  • 出版社/メーカー: シーアンドアール研究所
  • 発売日: 2019/08/10
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る


※追記※
スマホだと数式がうまく表示されない可能性がありますので、こちらのリンク、もしくはPCから購読頂けますと幸いです。


正解率の問題点と、偽陽性率と真陽性率

ROC・AUCに入る前に、それらを計算するための性能評価値についてまずは見ていきます。

ここでは、クラスaとクラスbを分類する二値分類問題を考えてみましょう。

ある訓練データを使って、クラスaとbを分類するモデルを作り、そのモデルを評価するためにテストデータに当てはめます。
正をaクラス、負をbクラスとしたとき、テストデータに当てはめた結果に対して以下のような図が書けますね。

識別クラス
真のクラス 正(a) 負(b)
正(a) True Positive
(TP:真陽性)
False Negative
(FN:偽陰性)
負(b) False Positive
(FP:偽陽性)
True Negative
(TN:真陰性)

縦軸がモデルの予測で、横軸が正解データになります。
各象限の説明を簡単にすると、

  • True Positive(TP): 正解データ正であるものを、正しく正と予測できた数
  • False Positive(FP):正解データ負であるものを、間違って正と予測した数
  • Flase Negative(FN):正解データ正であるものを、間違って負と予測した数
  • True Negative(TN):正解データ負であるものを、正しく負と予測できた数

となります。
※以降ですが、True PositiveはTP、False PositiveはFPといったように、表記を省略させて頂きますのでご了承ください。

さて、この表から正解率を求めるとすると、

正解率: (TP + TN)/(TP + FP + FN + TN)
という式で計算することができます。(分母が全体の数、分子が正解した数)

正解率を評価指標として用いるのが直感的には良さそうではあるのですが、クラスに偏りがある場合、機能しなくなるという問題があります。

なぜ不均衡なクラスでの正解率評価が問題か?

例えば、正解データがクラスa:80人、クラスb:20人のデータがあるとします。

もしあなたが「どんなデータもすべてクラスaに入るのだ!!!異論は認めないぞ!!」という大変めちゃくちゃなモデルを作ってしまったとします。

先ほどの表を作ってみると、

識別クラス
真のクラス TP:80 FN:0
FP:20 TN:0
となり、正解率を計算すると、
正解率: (TP + TN)/(TP + FP + FN + TN) = 80/ 100
という結果で、数字だけみればそこまで悪くありません。

しかし、これではモデルの精度が本当に良いとは言えないですよね。
このように、クラス間のデータ数に偏りがあると、データ数の多いクラスにとりあえず分類しておけば、単純に正解率は大きくなってしまいます。

ということで、クラス間の偏りに依存しない指標が必要になるわけです。

偽陽性率と真陽性率

もう一度この表を見てみましょう。
識別クラス
真のクラス 正(a) 負(b)
正(a) True Positive
(TP:真陽性)
False Negative
(FN:偽陰性)
負(b) False Positive
(FP:偽陽性)
True Negative
(TN:真陰性)

この表を使って下の2つの指標を算出してみたいと思います。

  • False Positive Rate(偽陽性率): FP/(FP + TN)
  • True Positive Rate(真陽性率): TP/(TP + FN)

「偽陽性率」は、正解データ負であるものを間違って正と予測した割合。(分母が正解データ負の総和)
「真陽性率」は、正解データ正であるものを正しく正と予測した割合。(分母が正解データ正の総和)

それぞれ各正解データのクラス内総データ数をもとに計算されるため、クラス間のデータ数の偏りによる影響はありません。

そして、(次節で掘り下げるますが)ROC曲線とは、そんな特徴を持つ「偽陽性率」と「真陽性率」をもとに算出する指標となります。

そのため、ROC曲線もクラス間のデータ数の偏りによる影響を受けないという特徴を持っています。

ROC曲線とAUCとは?

ROC曲線、AUCを算出するには、先程求めた偽陽性率と真陽性率を使います。
False Positive Rate(偽陽性率)を横軸にTrue Positive Rate(真陽性率)を縦軸に置いてプロットしたものがROC曲線です。

この説明だけだとよく分からないと思うので、具体例で見ていきましょう。
例えば、クラスaとクラスbの2つのクラスを分類する問題で、各データに対して、モデルの予測スコアが以下のようになったとします。

正解クラス score
a 0.8
a 0.45
a 0.7
b 0.2
b 0.4
a 0.5
b 0.15
a 0.65
b 0.2
b 0.5
a 0.6

こちらの表は、

  • 正解がaの、とあるデータをモデルに通したときの予測スコアが0.8
  • 正解がaの、とあるデータをモデルに通したときの予測スコアが0.45
  • 正解がaの、とあるデータをモデルに通したときの予測スコアが0.7
  • 正解がbの、とあるデータをモデルに通したときの予測スコアが0.2

…という風に、正解とスコアを並べたものです。

大体のモデルでは、「この予測スコアが 0.5以上ならaとする、0.5より小さかったらbとする」という風に閾値(しきい値)を決めて分類の判断を下します。

この閾値を色々変化させると、それに応じて偽陽性率と真陽性率も当然変化していきます。

例えば、「スコアが0.8以上のときクラスaと予測する、0.8より小さければbと予測する」とします。このとき、

識別クラス
正(a) 負(b)
真のクラス 正(a) TP:1 FN:5
負(b) FP:0 TN:5

となるため、偽陽性率は0, 真陽性率は1/6と求められますね。

次に、「スコアが0.45以上のときクラスaと予測する、0.45より小さければbと予測する」とします。このとき、

識別クラス
正(a) 負(b)
真のクラス 正(a) TP:6 FN:0
負(b) FP:1 TN:4

となるため、偽陽性率は0.2, 真陽性率は1と求められます。

このように各スコア値を閾値として、変化させたときに、各スコアに対する偽陽性率と真陽性率の関係をまとめたものが以下の表になります。(閾値よりも大きいスコアの場合、aと予測することを想定)

               
正解クラスscore 偽陽性率 真陽性率
a 0.8 0 0.17
a 0.450.2 0.1
a 0.7 0 0.33
b 0.2 0.8 1
b 0.4 0.4 1
a 0.5 0.2 0.83
b 0.15 1 1
a 0.65 0 0.33
b 0.2 0.8 1
b 0.5 0.2 0.83
a 0.6 0 0.67
うーん、これだけ見ても正直よくわからないですね……。
スコアの閾値を変えた時、偽陽性率と真陽性率がどのように変化しているのかをみるために、スコアを降順で並び替えてみます。                
正解クラスscore 偽陽性率 真陽性率
a 0.8 0 0.17
a 0.7 0 0.33
a 0.65 0 0.33
a 0.6 0 0.67
a 0.5 0.2 0.83
a 0.5 0.2 0.83
a 0.450.2 0.1
b 0.4 0.4 1
b 0.2 0.8 1
b 0.2 0.8 1
b 0.15 1 1

スコア(=クラス分類の閾値)を大きい値から下げていくと、偽陽性率と真陽性率がともに大きくなっていることがわかります。

閾値を下げるということは、なんでもかんでもとりあえずaと予測することを指しているので、陽性と判定される割合は大きくなる(真陽性率が大きくなる)と同時に、間違って陽性と判定する割合(偽陽性率が大きくなる)も高くなります。

ここで目指したいのは、間違って陽性と判定する割合を小さくして(偽陽性率が小さい)、なおかつ正解データの陽性であるものをできるだけ多く陽性と判定(真陽性率が大きい)できるモデルを作ることです。

ROC曲線をプロット

先程の偽陽性率と真陽性率の表をプロットすると以下のようなグラフが出来上がります。このように、閾値を変化させたときの偽陽性率と真陽性率による各点を結んだものがROC曲線です
f:id:gl2000-sans:20171118162758p:plain
(横軸:偽陽性率、縦軸:真陽性率)

偽陽性率(正解がクラスbのデータをクラスaと識別してしまった割合)が大きくなるように閾値を設定すれば、それはつまりクラスaに分類する判定を甘くすることを意味するため、真陽性率(正解がクラスaのデータをクラスaと識別できた割合)も自動的に上がります。つまり、このグラフは右下に下がることは決してなく、右に行くほど(偽陽性率が上がるほど)上に伸びる形をとります(真陽性率が上がる)。

極端に言えば、偽陽性率が1に近づくということは、なんでもかんでもクラスaに分類しているようなものなので、それは必然的に真陽性率も1に近づきますよね。

このようなことを考えると、良いモデルとは偽陽性率が低い時点で既に真陽性率が高い数値がでることという考えが直観的に理解できるのではと思います。

AUCの考え方

  • ROC曲線は右にいくほど下がることはない
  • 偽陽性率の値が小さい時点で、高い真陽性率を達成しているモデルほど良い

という2点を考えると、ROC曲線とx軸y軸で囲まれた部分(下図の斜線部)の面積ができるだけ大きいものほど良いモデルであると言えそうです。
f:id:Np-Ur:20171121235406p:plain

この面積の値がAUC(Area under an ROC curve)となります。AUCが1に近いほど性能が高いモデルとなり、完全にランダムに予測される場合、AUCは0.5、つまりROC曲線は原点(0,0)と(1,1)を結ぶ直線になります。

例えば2つのモデルを比較したいときに、ROC曲線が以下のようになったとします。
f:id:gl2000-sans:20171122095907p:plain
AUCが1に近いほど=ROCで囲われている面積の大きいほうがモデル性能が高いので、
この場合はモデル1の方が識別性能が高いという判断になります。

終わりに

今回はモデル性能の評価指標として有名なROC、AUCについてまとめてみました。

直感的に少しでもイメージを掴んで頂けましたら幸いです。ある程度直観的理解ができれば、実際はパッケージが自動的に求めてくれることが多いので、恐らく大丈夫です。

Python、RともにROC曲線のプロット、AUCの算出は簡単にできるので、ぜひ今後のモデル評価として使ってみてください。
(今後、時間があるときに実践編としてモデル評価をPythonとRでやるかもしれません、、、たぶん)