たつをの ChangeLog : 2013-08-28

多値分類・多クラス分類の結果を評価するために昔から使っている Perl スクリプトがあるので、整理して公開しておきます。これを状況にあわせて書き換えて使っています。別に難しいものではないので、その場でゼロから書いちゃうこともありますね。強引にPerlワンライナーでやったりとか。まあ、ブログに載せておけばそういう場面でもコピペで済むから楽になるな。

入力データ


評価用データは1事例1行で各行のフォーマットは下記の通り:
^[LABEL:SYS]\t[LABEL:ANS].*$
SYSはシステムの出力、ANSはもともとの正解のラベルを意味する。またラベルには "ans", "sys" という文字列を使わないこと。またはソースを書き換えること。

例を挙げます。ラベルは "P", "N", "M", "-" とします(後述のスクリプトもこれに合わせています)。

正解データファイルはこんな感じ。
N	これはひどいですね。
N	とっとと失せろ!
P	すばらしいアイディアです。
P	かっこいいよ
P	これはいいんじゃないっすか
P	楽しい苦行。
M	すごい!けど、ひどい!
M	うれしかなし。
-	電車に乗って移動中です。
-	鳩を見た

何かしらの分類システムを通し、結果を評価するためのファイル(評価用データ)はこういう感じにします。前述のフォーマットです。
N	N	これはひどいですね。
-	N	とっとと失せろ!
P	P	すばらしいアイディアです。
-	P	かっこいいよ
N	P	これはいいんじゃないっすか
M	P	楽しい苦行。
M	M	すごい!けど、ひどい!
-	M	うれしかなし。
-	-	電車に乗って移動中です。
-	-	鳩を見た

これが入力ファイル (prerec.test.txt) となります。

スクリプト


前述のフォーマットの入力ファイルを元に、Precision, Recall, F値と格平均値(マクロ平均)と Classification Accuracy を計算して出力します。print 文中のセパレータは chalow 用のテーブル記法に準拠していますが、適当にいじって使ってください。

prerec.pl :
#!/usr/local/bin/perl
use strict;
use warnings;

my %label;
my %count;
while (<>) {
    next if not /^([^\t\n]+)\t([^\t\n]+)(\t)/;
    my ($sys, $ans) = ($1, $2);
    $count{"ans"}{$ans}++;
    $count{$sys}{"sys"}++;
    $count{"ans"}{"sys"}++;
    $count{$sys}{$ans}++;
    $label{$sys} = $label{$ans} = 1;
}

my @labels = sort keys %label;
my $lb_num = @labels;

### Count
printf "|| %4s ||".(" %4s ||" x $lb_num)." %4s ||\n", "", @labels, "ans";
foreach my $l (@labels, "sys") {
    my @vals = map {$count{$_}{$l}||0} (@labels, "ans");
    printf "|| %4s ||".(" %4d ||" x $lb_num)." %4d ||\n", $l, @vals;
}
print "\n";

### Precision, Recall, F_1
my ($sum_pre, $sum_rec, $sum_f, $ok_res);
printf "|| %4s || %6s || %6s || %6s ||\n", "", "Pre", "Rec", "F";
foreach my $l (@labels) {
    my $pre = ($count{$l}{$l}||0)/($count{$l}{"sys"}||1);
    my $rec = ($count{$l}{$l}||0)/($count{"ans"}{$l}||1);
    my $f = 2 * $pre * $rec / (($pre + $rec)||1);
    printf "|| %4s || %.4f || %.4f || %.4f ||\n", $l, $pre, $rec, $f;
    $sum_pre += $pre;
    $sum_rec += $rec;
    $sum_f += $f;
    $ok_res += $count{$l}{$l}||0;
}
printf "||  Ave || %.4f || %.4f || %.4f ||\n", 
    $sum_pre/@labels,
    $sum_rec/@labels,
    $sum_f/@labels;
print "\n";
printf "|| Classification Accuracy || %.4f ||\n", $ok_res/$count{"ans"}{"sys"};
print "\n";

実行例


実行例です。入力データはさきほどの prerec.test.txt を使います。

% ./prerec.pl prerec.test.txt
||      ||    - ||    M ||    N ||    P ||  ans ||
||    - ||    2 ||    0 ||    0 ||    0 ||    2 ||
||    M ||    1 ||    1 ||    0 ||    0 ||    2 ||
||    N ||    1 ||    0 ||    1 ||    0 ||    2 ||
||    P ||    1 ||    1 ||    1 ||    1 ||    4 ||
||  sys ||    5 ||    2 ||    2 ||    1 ||   10 ||

||      ||    Pre ||    Rec ||      F ||
||    - || 0.4000 || 1.0000 || 0.5714 ||
||    M || 0.5000 || 0.5000 || 0.5000 ||
||    N || 0.5000 || 0.5000 || 0.5000 ||
||    P || 1.0000 || 0.2500 || 0.4000 ||
||  Ave || 0.6000 || 0.5625 || 0.4929 ||

|| Classification Accuracy || 0.5000 ||


実行結果を貼付けたもの(テーブル):

-MNPans
-20002
M11002
N10102
P11114
sys522110

PreRecF
-0.40001.00000.5714
M0.50000.50000.5000
N0.50000.50000.5000
P1.00000.25000.4000
Ave0.60000.56250.4929

Classification Accuracy0.5000

参考・関連


- 検索における適合率 (Precision) と再現率 (Recall)[2008-01-17-1]
- DCGによるスコア付きランキング出力結果の評価[2012-04-11-1]
- マクロ平均とマイクロ平均を混乱しがち[2006-12-12-3]

履歴


追記131021: ソースコードを改善。テストデータ、出力例も変更。ラベルをコードに記述していたのを不要にした。

たつをの ChangeLog
Powered by chalow