遅い→起動時

http://d.hatena.ne.jp/pmint/

ゼロから作る単語辞書 (1)

「1文あたり最長の1単語までしか検出しないようにしたら精度上がるんじゃないかな!?」と思って試してみた
d:id:pmint:20121012:p1の続き。

コード(Perl)
use utf8;
use strict;
use feature qw/switch/;
use Encode;
use Algorithm::Diff qw/sdiff/;

my @in = qw{
※ここに文章を書く
 ここに文章を書く
 ここに文章を書く
 ここに文章を書く
};

my %worddict;
my %sentencedict;

my $c;
foreach (@in){
    print STDERR "\r", ++$c, '/'.@in;
    learn($_);
}
print STDERR " done.\n\n";

{
    # 評価
    my %score;
    foreach my $k (keys %worddict){
        $score{$k} = ($worddict{$k} ** 2) * (length($k) - 1);
    }

    # 出力
    my $c;
    print "#: word, score\n";
    foreach my $k (sort { $score{$b} <=> $score{$a} } keys %score){
        print ++$c, ': ', encode('shiftjis', $k), ", $score{$k}\n";
    }
}

sub learn
{
    my($sentence) = @_;
    die unless not ref $sentence;
    
    foreach my $sentence2 (keys %sentencedict){
        
        # diff
        my $sdiff = sdiff([split //, $sentence], [split //, $sentence2]);
        
        my @words;
        my @unmodified;
        my $push = sub {
            push @words, join('', @unmodified);
            @unmodified = ();
        };
        
        foreach my $elem (@$sdiff){
            given ($elem->[0]){
                when ('u'){
                    die unless $elem->[1] eq $elem->[2];
                    push @unmodified, $elem->[1];
                }
                default {
                    $push->() if (@unmodified);
                }
            }
        }
        $push->() if (@unmodified);
        
        {
            # store
            my($longest_word) = sort { length $b <=> length $a } @words;
            $worddict{$longest_word}++;
        }
    }
    
    $sentencedict{$sentence} = 0;
}
結果
#: word, score
1: 忍たま, 1482642
2: 中心, 229441
3: 忍たま乱太郎, 92480
4: サークル, 8112
5: です。, 7442
6: 五年生, 6962
7: たま乱太郎, 6724
8: ほのぼの, 4800
9: たま, 4356
10: す。, 2916
11: 落第忍者乱太郎, 2646
12: 年生, 2209
13: 乱太郎, 2048
14: サークルです。, 1944
15: です, 1849
16: 中心です, 1728
17: ます。, 1682
18: 鉢屋三郎×不破雷蔵, 1568
19: 忍たま , 1323
20: 中心です。, 900
21: 三郎, 841
22: 中心に, 722
23: ギャグ, 722
24: オールキャラ, 605
25: 年生中心, 432
26: 中心で, 392
27: 中心。, 392
28: 漫画, 324
29: サークルです, 320
30: 落乱, 289
31: 食満, 196
32: 五年生中心, 196
33: ています, 147
34: 五年, 144
35: サークルで, 144
36: ています。, 144
37: タソガレドキ, 125
38: 六年, 121
39: 活動しています, 96
40: 竹谷, 81
41: ×食満留三郎, 80
42: てます。, 75
43: を中心に, 75
44: 食満留三郎, 64
45: こへ, 64
46: 鉢雷, 64
47: で活動しています, 63
48: 心です, 50
49: ります。, 48
50: 、五年生, 48
51: もあります。, 45
52: 善法寺伊作, 36
53: 小説, 36
54: もあります, 36
55: 雷蔵, 36

……(続く)……

2012-10-13.txt


…あんま変わってねえ('A`)

問題点

diffのアルゴリズム上、同じ文字が複数あるととにかく先に出現するもの同士を対応付けようとする。

入力文1: 落第忍者乱太郎/忍たま乱太郎
入力文2: 忍たま乱太郎

     ↓

[対応付け]
    落第忍者乱太郎/忍たま乱太郎
      忍      たま乱太郎

[こうなって欲しい]
    落第忍者乱太郎/忍たま乱太郎
            忍たま乱太郎

上記の「たま乱太郎」なんかがその結果で「落第者乱太郎」の後に「たま乱太郎」があると「忍」の対応付けがおかしくなってしまう。

「早く出現した者勝ち」ではなく、「共通部分が連続するような対応付け」ができないと「たま乱太郎」は無くならない。「忍たま乱太郎」のスコアも加算されない。


あとは遅いので入力できるデータ量も限られること。今のところ入力された文から2つ取り出す組み合わせを全てdiffにかけているので、uni-gram→文の辞書を作れば見込みのない文の組み合わせを省けそう。実際は文末の「。」や「です」「ます」の「す」があるからほぼ省かれないんだけど。