読者です 読者をやめる 読者になる 読者になる

コードネームは初話ユウ

自然言語処理でいろいろやってみる

cabochaソースを読む(3)各文節の素性

素性選択レイヤを見る。ソースはselector.cpp。l.117 parse()から行く。

 

l.121 の forループで(sentence中の)全chunkをなめる。

l.128 の forループで各chunkの中の全tokenをなめる。

各tokenに対して、

 

l.130 pat_kutouten_.match(token->normalized_surface)

これは何だろうか。parse()のすぐ上にSelector::open()があり、ここで

 

  CHECK_DIE(pat_kutouten_.compile(KUTOUTEN_PAT, &iconv));

 

としている。open()というくらいだから最初に1度呼ぶんだろう。この KUTOUTEN_PAT は、ソースの selector_pat.h の中で

 

// const char KUTOUTEN_PAT = "(。|、|,|.)";

 

のように定義されている。

match()の中身は追っていないが、まあパターンマッチしてくれるのだろう。

parse()に戻って、ll.130-143は、句読点・[開閉]かっこのパターンが見つかると片っ端から出力しているのがわかる。

 

素性選択レイヤの出力は↓こんな感じだが

 

* 3 -1D 0/1 0.000000 F_H0:本,F_H1:名詞,F_H2:一般,F_F0:を,F_F1:助詞,F_F2:格助詞,F_F3:一般,A:を,B:名詞-一般,G_CASE:を

 

G_PUNC:** や F_OB:** もこのような素性の例である。 

また、この"G_"とか"F_"とかは何か意味があるのかと気になるのだが、これもまた後ほど述べる。

 

l.148 findHead() の定義は下の l.238 にある。この関数は、文節の「主辞」と「機能辞」の位置を返す。中を見よう。

 

l. 253 head_matcher = &pat_ipa_head_;

のような行があるが、pat_ipa_head_ も pat_kutouten_/KUTOUTEN_PAT と同様 selector_pat.h に定義があり、

 

// const char IPA_HEAD_PAT = "!(助詞|助動詞|動詞,非自立|動詞,接尾|形容詞,非自

立|形容詞,接尾|空白|記号)";

 

要はこれらの品詞以外、つまりいわゆる自立語にマッチするようだ。ちなみに機能語は次のとおり:

 

// const char IPA_FUNC_PAT = "(助詞|助動詞|動詞,非自立|動詞,接尾|形容詞,非自立|形容詞,接尾)";

 

ll.266-271 を見るに、これらのパターンにマッチする自立語・機能語のうち、文節中の最後に表われるものをそれぞれこの文節の主辞・機能辞と判断し、その定義を返す。たとえば「約3キロ」なら最後の「キロ」が主辞になる、ということか。「転覆しそうだったのだけれど」なら最後の「けれど」が機能辞になる、と。係り受けを決定するのには「係り元の機能辞 -> 係り先の主辞」が重要なので、こうするのが効果的、ということなのだろう。

 

parse()に戻って、l.150あたりから。hXXは主辞(head)、fXXは機能辞(func)に関わる。

 

155    const char *hctype   = get_token(htoken, pos_size);

156    const char *hcform   = get_token(htoken, pos_size + 1);

 

pos_size は l.119で定義されてて、辞書がIPAなら4。IPA辞書では入力が

 

読ん    動詞,自立,*,*,五段・マ行,連用タ接続,読む,ヨン,ヨン

 

こうなので、[4]=段・マ行 [5]=連用タ接続 のようになるはず。つまりctypeが活用種類、cformが活用形だろう。

 

ll.195-205:

    if (pat_dyn_a_.prefix_match(ftoken->feature)) {

      ostrs << " A:" << fsurface;

    } else if (fcform) {

      ostrs << " A:" << fcform;

    } else {

      concat_feature(ftoken, pos_size, &output);

      ostrs << " A:" << output;

    }

 

    concat_feature(htoken, pos_size, &output);

    ostrs << " B:" << output;

 

 

また変なのが出てきた。pat_dyn_a_/DYN_A_PAT も selector_pat.h にあって、

 

// const char DYN_A_PAT = "(助詞|副詞|連体詞|接続詞)";

 

concat_feature()はutils.cppにあり、あるtokenのfeatureを全部ハイフンでつなげる。

 

つまり"A:"素性とは、機能辞が「これら4つのどれかの品詞なら文字列そのもの、活用語ならその活用形、それら以外なら品詞情報」となる。また"B:"素性とは「主辞の品詞情報」となる。

 

このA, Bとは何ぞや?は、係り受けparserを読まないとわからないのだが、先に簡単に説明しておくと、A素性とは「ある文節に既に係っている係り元の情報」、B素性は「ある文節から既に係っている係り先の情報」のことである。最初の回に挙げた論文にこの辺のことが「動的素性」として説明されているので参照のこと。

 

これは多分、「ひとつの述語にヲ格の補語は2つはつかない」のような制約をうまく表現できるのだろう。

 

selectorの段階ではまだどの文節がどの文節に係るかわかっていないが、まずは文節Xに対して「もしXが別の文節Dに係ったときは、DにつけるであろうA素性」と「もしXが別の文節Sから係られたときは、SにつけるであろうB素性」をリストアップしておく、という作業をselectorで行っているわけである。

 

ついでなので、最初の方で G_PUNCとかF_OBとか出てきたが、このG_やF_にも触れておく。G_は"gap feature"を表す。これは、「文節Sが文節Dにかかるとき、SとDの間に位置する文節の持つ情報」のことである。たとえば「私は彼の無垢な、あふれんばかりの才能に(ひそかに)嫉妬していた」という文で、「私は」が「嫉妬していた」に係るか?を考えるとき、途中に読点があるのでG_PUNCが、また開・閉かっこがあるので G_OB, G_CB がつくことになる。

 

F_は static feature といって、これは文節自体の持つ特徴である。

 

このように、cabochaではfeatureはstatic/gap/dyn_a/dyn_bの4種類ある。

これらをどのように使用しているか、は次回係り受けレイヤで見ていく。

 

 * * * *

ところでふと気づいたが、cabocha, KNP以外の係り受け解析器もあるみたい:

 

驚異的な解析速度を誇る日本語係り受け解析器

 

暇があったらみてみようか…まあそうそう他人のソースばかり読んでもいられないが。