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

コードネームは初話ユウ

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

cabochaソースを読む(1)処理の流れ概要

係り受けツールのcabochaをいじっているが、いろいろとひっかかってる。cabochaは係り受けをどうやって決めているのか?を知るため、ソースを少し読んでみた。以下そのメモ。

 

南瓜のページ http://code.google.com/p/cabocha/

 

からソースをDL。とったのは v0.64。このページにいろいろ基本的な解説があるので、まずそれを読んでおく。論文もあるので目を通しておく:

 

 チャンキングの段階適用による係り受け解析

 http://chasen.org/~taku/publications/nl142.pdf

 

tar.gz を展開。ソースは当然 src/ ディレクトリにある。

 

ソースを読み出す前に、まず「誰か既にソース読んで、わかったことブログに書いたりしてないかなぁ」などと怠慢なことを考えてざっと探すと、こんなのを発見:

 

Read Cabocha  http://hayashibe.jp/tr/

 

pdfが2つある。特に2つめの方は、いろいろ参考になる。これ以外には残念ながらソース読みに役立ちそうな情報は見つからなかった。

 

さてソースに行く。知りたいのは当面「係り受け決定にどんな特徴(素性)を使ってるのか」だけなのだが、さすがに処理の流れがわからないとどこにその情報があるのかもわからないので、まずはメイン関数から処理の流れをざっと追ってみる。

 

メイン関数 main() はいくつかあるが、普通のparse用のは cabocha.cpp にあるのがそれらしい。(他のは学習用とか、モデル作成用とか?)この main() の中では、 cabocha_do() を呼ぶだけ。

 

cabocha_do()の定義は parser.cpp の最後にある。ここのメインループ(l.512)は

  read_sentence() して parser.parseToString(str, size)

をひたすら繰り返す。1文ずつ、読んではパース、読んではパース、なんだろう。

 

read_sentence()の定義は utils.cpp にあるが、これは単に文字列を読み込むだけで難しい処理はしていない。

 

ちなみに入力フォーマットだが、まず cabocha には「レイヤ」という概念があることに留意。処理ステージ、みたいなもの。先の cabocha ページに解説があるが、

 1. 形態素解析(=mecab?)

 2. 文節区切り

 3. 素性選択

 4. 係り受け解析

とあり、それぞれの出力フォーマットも載っている。今の興味は係り受けの処理なので、文節区切りの出力を入力とすると思っておく。ならば'EOS'(=End of Sentence?)まで読み込めばよいことになる。なお1.2.4.はまあわかるとして、3.素性選択レイヤって何だ?と気になるところだが、これは後ほど。

 

さて parseToString(str, size) だが、これも parser.cpp にある。同じ名前がいくつかオーバーライドされててちょっとまぎらわしいが、今見るのはl.410の。要は parse(str, len) を呼んでから toString() で tree の内容を出力するみたい。ParserImpl クラスに tree_ というメンバ変数があり、parse() はその tree_ の内容を書き換える。Tree, Parser 両クラスの定義は cabocha.h にある。中身はおいおい見ていく。

 

parse()もオーバーライドがまぎらわしい。parse(str, len) の定義はl.367にあり、これは tree->read() してから parse(tree) する。2種類のparse()に留意のこと。

 

tree->read() は tree.cpp l.406 にある。詳細は後ほど書くが、ざっと見ると文字列を読みこんで tree のデータ構造に直しているようだ。

 

parse(tree) の定義は parser.cpp:l.352 に。analyzer_[i]->parse() をいくつかの i について順に呼ぶ。この analyzer_ とは何かと見ると、ParserImpl::open() が parser.cpp:l.197 で定義されていておそらく最初に呼ばれているのだが、ここで

 

310             PUSH_ANALYZER(Selector);

311             PUSH_ANALYZER(DependencyParser);

 

などとやっている。どうやら上述の各「レイヤ」に対して Analyzer クラスがあり、ここでpushしたAnalyzerを順に呼ぶ、ということのようだ。入力が文節区切り済みなら、その後の素性選択と係り受けだけやる、というわけ。

 

さて素性選択って何?だが、先のcabochaページに「素性選択レイヤの出力フォーマット」というのが載っている。これを見ると、こんな情報が付加されている。

 

* 2 -1D 0/2 0.000000 F_H0:読ん,F_H1:動詞,F_H2:自立,F_H5:五段・マ行,F_H6:連用タ接続,F_F0:いる,F_F1:動詞,F_F2:非自立,F_F5:一段,F_F6:基本形,A:基本形,B:動詞-自立

 

細かくは後で見るが、今は「ほー、素性としてこんな情報を使っているのか」程度に理解しておく。こういった情報を抽出するのが「素性選択」。

 

今回は大まかな処理の流れを見た。次回は具体的にtreeのデータ構造の中身を見ていく。