Inline::Perl5を使ったPerl 5のNaiveBayesとBenchで勝負してみる

こんにちは、Perl 6アドベントカレンダーの19日目の投稿になります。

今回はInline::Perl5を使ったモジュールとPure Perl 6なモジュールとの間で速度比較する方法を紹介したいと思います。

[去年のアドベントカレンダーにもありましたBench](http://qiita.com/B73W56H84/items/3c1b18ef60b4d1f381d5)を使います。

# 設定

+ 拙作[p6-Naive-Bayes](https://github.com/titsuki/p6-Algorithm-NaiveBayes)と[Perl 5のNaiveBayes](http://search.cpan.org/~kwilliams/Algorithm-NaiveBayes-0.04/lib/Algorithm/NaiveBayes.pm)の間で速度比較を行います。
+ テストセットには[情報検索の基礎のNaiveBayesの章で登場する例](http://nlp.stanford.edu/IR-book/html/htmledition/naive-bayes-text-classification-1.html#tab:nbtoy)を用います

# コード 

+ 単純にやってしまうと、Perl5とPerl6でモジュール名がかぶっていて競合します
+ Perl5の方で、`Inline::Perl5.run`の中で`use Algorithm::NaiveBayes`すると競合するのをさけられます(※もっといい方法あったら教えてください)

```pl:bayes.p6
use Bench;

my $repeat-times = 1000;
my $bench = Bench.new;

{ # Inline::Perl5
    use Inline::Perl5;
    my $p5 = Inline::Perl5.new;
    my $code = sub {
        my $result = $p5.run(q'                                                                                                                                          
            use Algorithm::NaiveBayes;                                                                                                                                   
            my $nb = Algorithm::NaiveBayes->new;                                                                                                                         
            $nb->add_instance(attributes => {Chinese => 2, Beijing => 1}, label => "China");                                                                             
            $nb->add_instance(attributes => {Chinese => 2, Shanghai => 1}, label => "China");                                                                            
            $nb->add_instance(attributes => {Chinese => 1, Macao => 1}, label => "China");                                                                               
            $nb->add_instance(attributes => {Chinese => 2, Tokyo => 1, Japan => 1}, label => "Japan");                                                                   
            $nb->train;                                                                                                                                                  
            $nb->predict(attributes => {Chinese => 3, Tokyo => 1, Japan => 1});                                                                                          
        ');
    }
    $bench.timethis($repeat-times,
                    $code,
                    title => 'P5');
}

{ # Perl 6 (plain text)

    my $code = sub {
        use Algorithm::NaiveBayes;
        my $nb = Algorithm::NaiveBayes.new();
        $nb.add-document("Chinese Beijing Chinese", "China");
        $nb.add-document("Chinese Chinese Shanghai", "China");
        $nb.add-document("Chinese Macao", "China");
        $nb.add-document("Tokyo Japan Chinese", "Japan");
        $nb.train();
        my @result = $nb.predict("Chinese Chinese Chinese Tokyo Japan");
    };

    $bench.timethis($repeat-times,
                    $code,
                    title => 'P6 (plain text)');
}

{ # Perl 6 Hash

    my %doc1 = "Chinese Beijing Chinese".words.Bag.hash;
    my %doc2 = "Chinese Chinese Shanghai".words.Bag.hash;
    my %doc3 = "Chinese Macao".words.Bag.hash;
    my %doc4 = "Tokyo Japan Chinese".words.Bag.hash;
    my %doc5 = "Chinese Chinese Chinese Tokyo Japan".words.Bag.hash;
    my $code = sub {
        use Algorithm::NaiveBayes;
        my $nb = Algorithm::NaiveBayes.new();
        $nb.add-document(%doc1, "China");
        $nb.add-document(%doc2, "China");
        $nb.add-document(%doc3, "China");
        $nb.add-document(%doc4, "Japan");
        $nb.train();
        my @result = $nb.predict(%doc5);
    };
    
    $bench.timethis($repeat-times,
                    $code,
                    title => 'P6 (Hash)');
}
```

# 実行結果

なんと、Perl5のNaiveBayesの方が7倍程度速い(Hash版で)結果になりました。
Inline::Perl5による変換コストがあっても、少なくとも数値計算系のモジュールならこっちのほうが速いということでしょうか。（あるいは自分の実装がいまいちか）

```bash:実行結果
$ perl6 bayes.p6
        P5: 0.4399 wallclock secs @ 2273.3782/s (n=1000)
P6 (plain text): 3.0702 wallclock secs @ 325.7150/s (n=1000)
 P6 (Hash): 2.7789 wallclock secs @ 359.8527/s (n=1000)
```

# 補足
## そもそもNaiveBayesって何に使うの？
+ NaiveBayesの主な使用用途は文書分類です。たとえば与えられたメールがスパムメールかどうか判定するのにつかわれます。(教科書的にはこういう説明になりますが、だいぶ古典的なアルゴリズムなので、現在のウェブメールのシステムで実際につかわれているかといえばおそらく使われていないと思います。)

## 計算結果について
+ あれ、Perl5版の方と計算結果が違うじゃないかと思った方がいるかと思います。
+ 拙作[p6-Naive-Bayes](https://github.com/titsuki/p6-Algorithm-NaiveBayes)は[情報検索の基礎のNaiveBayesの章で登場する例](http://nlp.stanford.edu/IR-book/html/htmledition/naive-bayes-text-classification-1.html)でverifyされています。

```
$ perl6 -e '(3/4 * (3/7) ** 3 * 1/14 * 1/14).log.say'
-8.10769031284391 # Chinese
$ perl6 -e '(1/4 * (2/9) ** 3 * 2/9 * 2/9).log.say'
-8.90668134500126 # Japan
```


以上、Perl 6アドベントカレンダーの19日目の投稿でした。

2016/12/18追記：
・若干Perl5の方が有利な設定になっていたので、Perl6の方でHash版も追加しました
・Hash版で`.words`が抜けてたので追加して測りなおしました。すみません。