クロの制作日記

クロの制作日記

田舎の大学生がUnityとか機械学習関連の制作物をひたすらアップします。ブログで紹介したコード一覧https://github.com/kuroshum/blog_code

二値分類における評価指標(混同行列・Precision・Recall・F値・ROC曲線・AUC)

はじめに

現在、大学で二値分類を扱う研究を進めています。その調査として、色々二値分類に関する知識が蓄えられたので、ここで放出しておきます。
ただ、書いている途中で気づいたのですが、全部1つの記事に書くと余裕で1万文字を超えて見づらくなるので、記事を分割することにしました。

1つ目:二値分類における評価指標(この記事)
2つ目:pAUC最大化

こんな感じで分けました。pAUC最大化の話が見たい人は上のリンクから飛んでください(まだ公開していません)

公開しました。
kurora-shumpei.hatenablog.com


取り敢えずこの記事の目次です。



二値分類とは?

どうせなので、そもそも二値分類とは何かという所から入っていこうかと思います。そんなもんわかっとるわい、という人は適当に読み飛ばしてください。


二値分類とは、入力として与えられたデータをPositive(正)かNegative(負)かを判別してその結果を出力するというものです。
簡単な例をあげると、以下の画像のように与えられた画像が猫であるか否かを判別するようなものです。

f:id:kurora-shumpei:20190528030646p:plain
猫の二値分類
(画像の猫は大学に住み着いてる猫です。下の画像は私が作ったぶり大根です。)


この分類器が二値分類を行う手法はたくさんあります。昔からある手法としては、SVMやk-means法、ナウい手法としては、ニューラルネットワークやディープニューラルネットワークなどなど。他にも分類を行う手法はたくさんありますので、興味がある人はこのあたりの記事を読んでみてください。
qiita.com

この記事では、取り敢えずニューラルネットワークを用いた分類器を考えることにします。

ニューラルネットワークを用いた二値分類の場合、分類器は各入力されたデータ(今回の場合は画像)から、どれだけ猫らしいかの確率を出力し、その確率が設定した閾値(0.5とか)より大きかったら猫と判定、小さかったら猫じゃないと判定、と言った風に二値分類を行います。

評価指標

ニューラルネットワークを用いて分類器を作成したとします。では、その作成した分類器がどれくらいの精度があるのか、といった評価はどのようにすれば良いのでしょうか?

機械学習でよく用いられる評価指標の1つとして「Accuracy」という指標があります。

Accuracy

Accuracyとは日本語でいうと「成功率」のことです。

分類器にデータを入力し、その分類結果がどれくらい成功していたかを表す単純な指標です。

例えば、100個のデータが入力されたときに、90個のデータにおいて分類が成功した場合はその分類器のAccuracyは90%となります。なので、このAccuracyの値が大きければ大きいほど分類器の精度が良いということになります。

一見、正確に精度を測れているように見えますが、この指標には一つ問題があります。


例えば、猫の画像かそうでないかの分類を行うときに、入力の画像に猫の画像が90個含まれている場合を考えてみましょう。このとき、分類器がすべての入力の画像に対して、「この画像は猫!」と出力したとします。この分類器は猫という特徴を全く捉えられていないので精度はあまり良いとは言えませんよね。

ただ、それにも関わらず、Accuracyは90%という値になってしまいます。要するに、入力データに偏りがある場合にはAccuracyという指標はあまり使いものになりません。

なので、こういった場合では別の指標が使用されます。

混合行列

まずは以下の図を見てください。

真値 \ 予測値 Positive Negaitve
Positive True Positive False Negative
Negative False Positive True Negative

この図は混同行列と呼ばれるもので、二値分類における、予測値と真値との関係を表しています。

PositiveとかNegativeとかいっぱいありますが、それぞれ、「猫である」と「猫ではない」といった風に解釈すればわかりやすいかと思います。

この行列のそれぞれの要素の意味は以下のようになっています。

True Positive

予測値がPositiveであり真値もPositiveであった場合。
(入力された画像が猫だと予測したら猫の画像だった。)

False Positive

予測値がPositiveであるが、真値はNegativeであった場合。
(入力された画像が猫だと予測したら猫の画像じゃなかった。)

False Negative

予測値がNegativeであるが、真値はPositiveであった場合。
(入力された画像が猫じゃないと予測したら猫の画像だった。)

True Negative

予測値がNegativeであり真値もNegativeであった場合。
(入力された画像が猫じゃないと予測したら猫の画像じゃなかった。)

この関係は私もよくこんがらがるので、分からなくなったらこの関係図を見直しましょう。

そして、今考えている、出力がすべてPositiveになってしまっている分類器の混同行列を見てましょう。

真値 \ 予測値 Positive Negaitve
Positive 90 0
Negative 10 0

この行列を見ると、True Positiveの割合は高いが、True Negativeの割合は極端に低くなってしまっているのが確認できます。このようにして、分類器の精度を確認することができます。

ただ、一々、この行列の関係性を見るのは面倒なので、こういった関係性を数値として表現する指標があります。

Precision・Recoll・F値

とりあえず、PrecisionとRecollの説明からしていきます。

Precision(適合率)

Positiveと予測したデータのうち、本当にPositiveであった確率のことを指します。

 Precision(適合率) = \frac{TP}{TP + FP}

Recoll(再現率)

Positiveなデータのうち、Positiveだと予測された確率のことを指します。

 Recoll(再現率) = \frac{TP}{TP + FN}


こりゃまたこんがらがりそうな指標がでてきましたね。

この二つの指標の違いは、Precisionは予測値、Recallは真値に焦点をあてているという点です。

f:id:kurora-shumpei:20190529030434p:plain
Precision & recallの焦点

なので、Precisionは予測値がどれくらい正確であるかを見ており、False Negative(予測値がNegativeであるが、真値はPositiveであった場合)を考慮しません。

一方で、Recallは予測値がどれくらい網羅できているかを見ており、False Positive(予測値がPositiveであるが、真値はNegativeであった場合)を考慮しません。


さらに、PrecisionとRecallはトレードオフの関係にあります。

トレードオフとはどういう意味かというと、一方の値が向上するともう一方の値が減少することを指します。要するに、Precision(Recall)の値が向上したら、Recall(Precision)の値が減少するということです。

なので、このPrecisionとRecallがどちらもある程度良いスコアを出している分類器が理想的な分類器となります。そして、そのバランスの良さを測る指標というものが存在し、F値と呼ばれています。




F値

F値は以下の式で定義されます。

 F値 = \frac{2 * Precision * Recall}{Precision + Recall}

この指標は、先ほど述べたように、PrecisionとRecallのバランスの良さを測るためにPrecisionとRecallの調和平均を取ったものとなっています。分類器の精度を総合的に出したいときにはこの指標を使うと良いかと思います。

scikit-learnで実装

これらを一々プログラム上で計算するのは面倒ですが、scikit-learnを利用すれば、勝手に計算してくれる関数を使うことができます。

Acuraccy

Accuracyを計算する関数はaccuracy_scoreという関数です。
scikit-learn.org

>>> from sklearn.metrics import accuracy_score
>>> true_data = [1,1,1,1,1,1,0,1,1,1] # 1:poisitive 0:negative
>>> prediction_data = [1,1,1,1,1,1,1,1,1,1]
>>> accuracy_score(true_data, prediction_data)
混同行列

混同行列を計算する関数はconfusion_matrixという関数です。
scikit-learn.org

>>> from sklearn.metrics import confusion_matrix
>>> true_data = [1,1,1,1,1,1,0,1,1,1] # 1:poisitive 0:negative
>>> prediction_data = [1,1,1,1,1,1,1,1,1,1]
>>> confusion_matrix(true_data, prediction_dat)
Precision

Precisionを計算する関数は.precision_scoreという関数です。
scikit-learn.org

>>> from sklearn.metrics import precision_matrix
>>> true_data = [1,1,1,1,1,1,0,1,1,1] # 1:poisitive 0:negative
>>> prediction_data = [1,1,1,1,1,1,1,1,1,1]
>>> precision_matrix(true_data, prediction_dat)
Recall

Recallを計算する関数はrecall_scoreという関数です。
scikit-learn.org

>>> from sklearn.metrics import recall_matrix
>>> true_data = [1,1,1,1,1,1,0,1,1,1] # 1:poisitive 0:negative
>>> prediction_data = [1,1,1,1,1,1,1,1,1,1]
>>> recall_matrix(true_data, prediction_dat)
F値

F値を計算する関数はf1_scoreという関数です。
scikit-learn.org

>>> from sklearn.metrics import f1_matrix
>>> true_data = [1,1,1,1,1,1,0,1,1,1] # 1:poisitive 0:negative
>>> prediction_data = [1,1,1,1,1,1,1,1,1,1]
>>> f1_matrix(true_data, prediction_dat)

ROC曲線

混同行列を利用した評価指標としてROC曲線というものもあります。

ROC曲線は横軸にFalse Positive Rate、縦軸にTrue Positive Rateを取ったグラフのことです。

f:id:kurora-shumpei:20190530093510p:plain
ROC曲線

これだけだとイメージが掴めないと思いますので、適当な例を上げて説明していきます。

以下の画像のように、猫かどうかを判別する分類器に異なる5つの画像を入力したとしましょう。そして、分類器は入力された各画像に対して、どれくらい猫であるかの確率を出力します。

f:id:kurora-shumpei:20190529204044p:plain
猫らしさの確率を出力する分類器

分かりやすいように画像とスコアの対応関係を表にしておきました。
(Aの画像はほんとは猫ですが、便宜上タヌキということにしています(猫よすまぬ)。)

Score
A(タヌキ) 0.6
B(猫) 0.9
C(ステーキ) 0.1
D(猫) 0.4
E(猫) 0.8

二値分類では、それぞれ出力された確率から閾値を設定し、その閾値より大きければPositive(猫)、それ以下ならNegative(猫じゃない)みたいに分類を行います。

例えば、閾値を0.6に設定したと仮定して、上の画像の場合での混同行列を考えてみましょう。

0.6を閾値に設定したときの混同行列
真値 \ 予測値 Positive Negaitve
Positive 2 1
Negative 1 1

スコアが0.6以上の画像はA,B,Eの3つで、B,Eは猫の画像ですのでTrue Positiveは2、False Positiveが1となりますし、スコアが0.1のDはステーキの画像で猫でないのでTrue Negativeが1となります。ただ、スコアが0.4にも関わらず猫の画像であるDは、猫の画像ではないと判定されてしまいますので、False Negativeが1となります。

同じように閾値を0.9に設定したときの混同行列は以下のようになります。

0.9を閾値に設定したときの混同行列
真値 \ 予測値 Positive Negaitve
Positive 1 2
Negative 0 2

このように、閾値を各入力画像に対するスコアに設定していき、True Positive RateとFalse Negative Rateを計算した表が以下のようになります。

True Positive Rate False Positive Rate Score
A 0.66 0.5 0.6
B 0.33 0 0.9
C 1 1 0.1
D 1 0.5 0.4
E 0.66 0 0.8

Scoreで降順にソートします。

True Positive Rate False Positive Rate Score
B 0.33 0 0.9
E 0.66 0 0.8
A 0.66 0.5 0.6
D 1 0.5 0.4
C 1 1 0.1

この表を折れ線グラフとしてプロットしたものがROC曲線となります。

f:id:kurora-shumpei:20190530093510p:plain
ROC曲線

要するに、ROC曲線というのは、各入力に対しての出力(Score)を閾値と設定し、それぞれのTrue Positive RateとFalse Positive Rateをグラフに起こしたグラフとなります。

ROC曲線の性質

基本的に、ROC曲線は上のグラフのように、False Positive Rateが向上すればTrue Positive Rateが向上するようになっています。

このようになるのは、閾値が変化することでTrue Positive RateとFalse Positive Rateも変化することが原因です。

閾値が低い

  • 大半のデータをPositiveと判定

  => True Positve Rateは向上

  • NegativeなデータをPositiveと判定

  => False Positive Rateも向上

なので、閾値が低いとグラフ上の点は右上の方に分布します。

閾値が高い

  • 大半のデータをNegativeと判定

  => True Positve Rateは減少

  • NegativeなデータをPositiveと判定が少なくなる

  => False Positive Rateも減少

なので、閾値が高いとグラフ上の点は左下の方に分布します。

一般的に精度の良い分類器というのは、True Positive Rateが高くて、False Positive Rateが低い分類器のことを指します。よって、True Positive RateとFalse Positive Rateの関係を考慮すると、そういった精度の良い分類器を作ることの難しさがわかるかと思います。

AUC

AUCはROC曲線の下側の面積のことです。以下のグラフでいうと、オレンジ色の部分の面積です。

f:id:kurora-shumpei:20190530093539p:plain
AUC(赤い部分の面積)

なぜ、こういった面積を考えるかというと、このAUCの値を計算することで分類器の精度を測ることができるからです。

精度の良い分類器というのは、False Positive Rateが小さくてもTrue Positive Rateが大きい値をとっているものでしたよね。

そういった値を取ると以下のグラフのように、AUCの値は大きくなります。

f:id:kurora-shumpei:20190531165551p:plain
精度の良い場合のAUC

逆に、False Positive Rateが高くてTrue Positive Rateが小さいと以下のグラフのように、AUCが小さくなります。

f:id:kurora-shumpei:20190531165637p:plain
精度の悪い場合のAUC

このように、AUCの値を計算することで分類器の精度計算を行うことができますのでよく用いられます。

scikit-learnで実装

ROCはグラフなので関数で計算することができませんが、AUCなら関数が用意されています。roc_auc_scoreという関数です。
scikit-learn.org

>>> from sklearn.metrics import roc_auc_score
>>> true_data = [1,1,1,1,1,1,0,1,1,1] # 1:poisitive 0:negative
>>> prediction_data = [1,1,1,1,1,1,1,1,1,1]
>>> roc_auc_score(true_data, prediction_dat)




最後に

今回は二値分類における評価指標について、ご紹介しました。

次の記事では、分類器の学習にAUCを利用する手法の紹介いたします。
kurora-shumpei.hatenablog.com