多クラス分類の結果を評価するための雛形スクリプト (Perl)
2013-08-28-1
[Programming][Algorithm]
多値分類・多クラス分類の結果を評価するために昔から使っている Perl スクリプトがあるので、整理して公開しておきます。これを状況にあわせて書き換えて使っています。別に難しいものではないので、その場でゼロから書いちゃうこともありますね。強引にPerlワンライナーでやったりとか。まあ、ブログに載せておけばそういう場面でもコピペで済むから楽になるな。
評価用データは1事例1行で各行のフォーマットは下記の通り:
例を挙げます。ラベルは "P", "N", "M", "-" とします(後述のスクリプトもこれに合わせています)。
正解データファイルはこんな感じ。
何かしらの分類システムを通し、結果を評価するためのファイル(評価用データ)はこういう感じにします。前述のフォーマットです。
これが入力ファイル (prerec.test.txt) となります。
前述のフォーマットの入力ファイルを元に、Precision, Recall, F値と格平均値(マクロ平均)と Classification Accuracy を計算して出力します。print 文中のセパレータは chalow 用のテーブル記法に準拠していますが、適当にいじって使ってください。
prerec.pl :
実行例です。入力データはさきほどの prerec.test.txt を使います。
実行結果を貼付けたもの(テーブル):
- 検索における適合率 (Precision) と再現率 (Recall)[2008-01-17-1]
- DCGによるスコア付きランキング出力結果の評価[2012-04-11-1]
- マクロ平均とマイクロ平均を混乱しがち[2006-12-12-3]
追記131021: ソースコードを改善。テストデータ、出力例も変更。ラベルをコードに記述していたのを不要にした。
入力データ
評価用データは1事例1行で各行のフォーマットは下記の通り:
SYSはシステムの出力、ANSはもともとの正解のラベルを意味する。またラベルには "ans", "sys" という文字列を使わないこと。またはソースを書き換えること。^[LABEL:SYS]\t[LABEL:ANS].*$
例を挙げます。ラベルは "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 ||
実行結果を貼付けたもの(テーブル):
- | 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 |
参考・関連
- 検索における適合率 (Precision) と再現率 (Recall)[2008-01-17-1]
- DCGによるスコア付きランキング出力結果の評価[2012-04-11-1]
- マクロ平均とマイクロ平均を混乱しがち[2006-12-12-3]
履歴
追記131021: ソースコードを改善。テストデータ、出力例も変更。ラベルをコードに記述していたのを不要にした。
この記事に言及しているこのブログ内の記事