KNPの出力結果を眺めて、どうやって解析すればいいのか悩んでいたのですが、KNPに同梱のPerlライブラリでXML出力をサポートしているのを発見して安心しました。(「perldoc KNP::Result」すると書いてある。)
CaboChaができない照応解析をする必要があってKNPをインストールしたので、早速、人称代名詞を人称代名詞が指し示す人物名に置換するプログラムを書いてみました。
#!/usr/bin/env perl use strict; use warnings; use utf8; use feature qw/say/; use open qw/:encoding(utf-8) :std/; use KNP; use XML::LibXML; use List::MoreUtils (); use Lingua::JA::NormalizeText; # 入力文に半角スペースが含まれているとKNPが死ぬ my $normalizer = Lingua::JA::NormalizeText->new(qw/space_h2z/); my $xml = XML::LibXML->new; $xml->no_network(1); my @documents; push(@documents, ['安倍 晋三と小泉純一郎は麻生太郎がグアテマラコーヒーを買うのを見た。', '彼はそれを飲んだ。', '晋三は彼を見て喉の乾きを感じた。']); push(@documents, ['勅使河原と明日香は金沢の本屋さんに行った。', '彼が小説を立ち読みしていると、彼女がお漏らししてしまった。', '勅使河原はひどく狼狽していた。']); for my $document (@documents) { my $knp = KNP->new(option => '-tab -anaphora'); # 文書毎にリセットする my %chara_candi_list_of_entity_id; # 登場人物候補リスト for my $sentence (@{ $document }) { my $input = $normalizer->normalize($sentence); say "入力文:$input"; print "出力文:"; my $result = $knp->parse($input); if ($result) { my $dom = $xml->parse_string($result->all_xml); my @phrase_nodes = $dom->findnodes('//sentence/phrase'); for my $phrase_node (@phrase_nodes) { my $fstring = $phrase_node->findvalue('@fstring'); my $entity_id; if ($fstring =~ /<EID:(.+?)>/) { $entity_id = $1; # 共参照関係にある基本句には同じIDが付与される if ($fstring =~ /<NE:(?:PERSON|LOCATION):(.+?)>/) { push(@{ $chara_candi_list_of_entity_id{$entity_id} }, $1); } } my $is_coref = 0; $is_coref = 1 if $fstring =~ /<COREFER_ID:.+?>/; my @word_nodes = $phrase_node->findnodes('node/word'); for my $word_node (@word_nodes) { my $word = $word_node->findvalue('.'); my $bunrui = $word_node->findvalue('@bunrui'); if ( $is_coref && ($bunrui eq '普通名詞') ) { $word = ${ $chara_candi_list_of_entity_id{$entity_id} }[-1]; } print $word; } } print "\n"; #print $result->all_xml; } else { die 'なんかエラー: ' . $knp->error; } } print "\n"; say '登場人物候補:'; for my $entity_id (sort keys %chara_candi_list_of_entity_id) { say "EID:$entity_id: " . join(' / ', List::MoreUtils::uniq @{ $chara_candi_list_of_entity_id{$entity_id} }); } print "\n"; }
Replacement list is longer than search list at /Users/username/.anyenv/envs/plenv/versions/5.22.0/lib/perl5/site_perl/5.22.0/Lingua/JA/Moji.pm line 861. Replacement list is longer than search list at /Users/username/.anyenv/envs/plenv/versions/5.22.0/lib/perl5/site_perl/5.22.0/Lingua/JA/Moji.pm line 868. 入力文:安倍 晋三と小泉純一郎は麻生太郎がグアテマラコーヒーを買うのを見た。 出力文:安倍 晋三と小泉純一郎は麻生太郎がグアテマラコーヒーを買うのを見た。 入力文:彼はそれを飲んだ。 出力文:麻生太郎はそれを飲んだ。 入力文:晋三は彼を見て喉の乾きを感じた。 出力文:晋三は晋三を見て喉の乾きを感じた。 登場人物候補: EID:2: 安倍 晋三 / 晋三 EID:4: 小泉純一郎 EID:6: 麻生太郎 EID:7: グアテマラ 入力文:勅使河原と明日香は金沢の本屋さんに行った。 出力文:勅使河原と明日香は金沢の本屋さんに行った。 入力文:彼が小説を立ち読みしていると、彼女がお漏らししてしまった。 出力文:明日香が小説を立ち読みしていると、明日香がお漏らししてしまった。 入力文:勅使河原はひどく狼狽していた。 出力文:勅使勅使河原はひどく狼狽していた。 登場人物候補 EID:2: 勅使河原 EID:3: 明日香 EID:4: 金沢
Perl 5.22.0 にアップデートしてから「Lingua::JA::Moji」が警告を吐くようになってしまったので、暇があれば作者にプルリクエストしておきましょうかね。
照応解析では、名前の女性名らしさや男性名らしさなどはさすがに考慮してくれないようなので、それも考慮させたい場合は、自前でなんとかしないといけません。
照応解析誤りが目立ちますが、「安倍 晋三」と「晋三」を同一人物と推定してくれるだけでも非常に助かります。
KNPの固有表現抽出と照応解析は以下が詳しいです。