プログラミング

【MATLAB】perfcurveを使わずROC曲線を描く

投稿日:

こんにちは。

整形外科医師ブロガーのボククボです。

今回もMATLABについてのプチ情報です。

MATLAB分類学習器アプリを使えば、

ワンクリックで、20種類以上のアルゴリズムで
機械学習をして、分類器を作成してくれます。

perfcurveという関数もあり、

もうワンクリックすると、ROC曲線も描いてくれます。

とても便利なのですが、
さすがに何を計算しているのか気になります。

今回は、perfcurve関数の中身を確認するため、

perfcurveなしでROC曲線を描いてみます。

ROC曲線とは

ROC曲線とは、

Receiver Operating Characteristic曲線の略です。

今回の本題ではないのでさらっといきますが、

ある検査/分類器について、

横軸に偽陽性率、縦軸に真陽性率をとるグラフで、
しきい値(=陽性判定の基準値)を
任意に変化させたときに描かれる曲線

です。

機械学習では、
分類器の性能を評価するのに
用いられます。

水色の面積(AUC)が大きいほど
分類器の性能が高いことをあらわすことになります。

分類器/検証用データの例

ROC曲線は分類器の性能を評価する曲線です。

ですので、

評価対象の分類器と、検証用データが必要になります。

分類器

分類器は、適当にアプリで作りました。

線形サポートベクトルマシンです。

classificationSVM = fitcsvm(...
    predictors, ...
    response, ...
    'KernelFunction', 'linear', ...
    'PolynomialOrder', [], ...
    'KernelScale', 'auto', ...
    'BoxConstraint', 1, ...
    'Standardize', true, ...
    'ClassNames', [0; 1]);

学習用データpredictors,responseは提示していませんが、

4つの特徴量をいれると、'0'か'1'かを判定してくれる分類器です。

この分類器にテストデータをいれて

性能を評価してみようと思います。

検証用データ

今回は特徴量4つなので、

testdata=(100×5の配列)にしてみました。

被検者100人について、

特徴量:1-4列
5列目:'1'、'0'の教師情報(真のクラス)

をいれたデータです。

('1'=陰性、'0'=陽性と思ってください。)

このデータの
予測結果と、真のクラスから、

ROC曲線を描いて、分類器の性能を評価します。

ROC曲線を描くのに必要なデータ

まず、perfcurveを使ってみることを考えます。

必要な引数は3つあります。

[X,Y] = perfcurve(label,scores,posclass)

であり、

label:真のクラス=testdataの5列目
posclass:陽性のラベル=0

です。

あとはscoreですね。

分類スコア、事後確率

scoreに入る項目は
分類器のアルゴリズムによって異なりますが、

サポートベクトルマシンでは、
分類スコアと呼ばれる数値が入ります。

単純に言えば、

「分類の境界線からの符号付の距離」

になります。

しきい値が既定値なら、
分類スコア>0なら陽性
分類スコア<0なら陰性

です。

ベイズ分類器では、事後確率の大小関係で
分類しますので、

scoreには事後確率(0~1の範囲)が入ります。

しきい値が既定値なら

事後確率>0.5なら陽性
事後確率<0.5なら陰性

です。

実際の計算は、matlabにお任せで

%testdataから分類スコアを計算して予測。予測結果は~で省略。
%classificationSVMは分類器
[~,score]=predict(classificationSVM,testdata(1:4))

で計算してくれます。

ROC曲線を図示

さて、

perfcurveで計算したX,Yをplot(X,Y)で図示すると

が得られます。

実用上はこれで問題ないですが、
これから、どのようにしてこの曲線が得られるか
見ていきます。

分類スコアの昇順にデータを並べ替え

まずは、分類スコアと真のクラスを一つの表にまとめます。

%学習済み分類器を使って、各テストデータの分類スコアをゲットする
%scoreの1列目は'0'に分類する分類スコア、2列目は'1'と判定する分類スコア
[~,score]=predict(classificationSVM,testdata(:,1:4));

% 分類スコアと真のクラスをテーブルでドッキング
score_class=table;

score_class.score=score(:,1);
score_class.true_Class=testdata(:,5);

そして、この表をscoreの小さい順に並べ替えます。

%1列目のscoreによって昇順に並べ替える
score_class=sortrows(score_class);

しきい値を被検者1人分づつ変えていく

ROC曲線では、分類スコアのしきい値を変えつつ

真陽性率/偽陽性率を計算していきます。

しきい値の変化のさせ方は無限にありますが、

各被検者の分類スコアを超えない変化だと、
どのみち真陽性率/偽陽性率は変化しませんので

しきい値の変え方は、

「各被検者の中間」

にすれば十分です。

サポートベクトルマシンのしきい値は
既定値では0です(下図)

にしています。

しきい値をかえつつ計算

真陽性率/偽陽性率は定義に従い
計算するだけです。

%分類器しきい値を1被検者分ずつずらして、
%逐一真陽性率/偽陽性率を計算
num=height(score_class);
%1列目に真陽性率、2列目に偽陽性率を格納する予定で宣言
true_falsePosRate=zeros(num,2);

for i=1:num
    %真の'0'の数、真の'1'の数
    true_count=nnz(score_class{:,2}==0);
    false_count=nnz(score_class{:,2}==1);
    
    %真に'0'で分類も'0'/真には'1'だけど分類は'0'の数
    pred0andTrue=nnz(score_class{i:num,2}==0);
    pred0butFalse=nnz(score_class{i:num,2}==1);
    
    %真陽性率/偽陽性率を計算
    true_falsePosRate(i,1)=pred0andTrue/true_count;
    true_falsePosRate(i,2)=pred0butFalse/false_count;   
end

図示

上で計算したデータから、ROC曲線を描きます。

横軸は偽陽性率、縦軸は真陽性率として
プロット(答え合わせするのでscatter)
してみます。

scatter(true_falsePosRate(:,2),true_falsePosRate(:,1));

となりました。

一致していますね。

まとめ

matlabには便利な関数がたくさんありますが、

内容がわかっていると、

変則的な処理の際に、応用が効きそうです。

検証用データの
「分類スコア」と「真のクラス」が
分かっていれば

ROC曲線が描ける

ということが実感できたかと思います。

「ROC曲線をかけ」

といわれて固まっている人がいたら

まず「分類スコア/事後確率」を求めてください。

-プログラミング
-, , , , ,

Copyright© ボククボのブログ , 2020 All Rights Reserved Powered by STINGER.