【自然言語処理】知識の構造化に向けた第一歩としての構文解析

Rで係り受け解析をやって語と語の関係を有向グラフにしよう!

wanko-sato.hatenablog.com

と思い立ったはいいのですが、これが考え出すとなかなかやっかいでして。そもそもどういうレベルで語と語の「関係」を定義づければいいのか、が自分の中であいまいだったようです。なので、ここで改めて、きちんと考え直してみようと思います。
※今回はプログラミングの話はありません。

まずは例で考える。

私は傘を買った。

という文の場合、

私は||傘を||買った

という三つの文節に区切ることができます。この場合、「私は」が主語、「傘を」が目的語、「買った」が述語に当たります。この場合、それぞれはどのような関係にある、と言えるでしょうか?

名詞動詞を関係づける場合

自分は最初、この方式で考えていました。こうすれば名詞同士の関係がわかりやすく、すっきりしたグラフにできそうだと考えたからです。

「私」と「傘」は「買った」という動詞で関係づけられています。ざっくりした図にすると、

私-(買った)→傘

という形になります。グラフでいうと「私」というノードから「傘」というノードに伸びた有向エッジが「買った」というattributeを持つ、というものになります。例えばこの後で「私」が「傘」を「さした」とすると、「私」から「傘」に有向エッジが伸びている、というグラフの形状は変化しませんが、有向エッジに「さした」というattributeが付け加わることになります。
これは名詞同士のつながりをグラフにするものですので、すっきりしたグラフになることが予想されます。
ただし、自動詞の場合や主語と形容詞のみの場合にどうするのか、が問題になります。

彼は怒った。

この場合、名詞は「彼」のみなので、エッジのない、単独のノードのみのグラフになります。あるいは自分に向かって「怒った」というattributeをもったエッジを伸ばした自己ループのグラフにすることも考えられます。
もう一つの考え方として、自動詞や形容詞はノードに対するattributeとするやり方もあります。ただ、そうなるといろいろな情報を落としてしまうことになりそうです。

フランス革命は18世紀にフランスで起こった。

この場合、主語「フランス革命」はノードとして確保するとして、「18世紀」や「フランス」はノードとして扱うべきでしょうか?それともattributteとして扱うべきでしょうか?いずれも名詞ではありますが、「起こった」に対する補足であり、attributeとして扱っても支障はなさそうに見えてしまいます。そのような定義のあいまいさは後々問題になるので、細かな場合分けが必要なやり方よりも、いっそのこと一律に扱ってしまった方が簡単そうです。

文節同士を関連付ける場合

再び最初の例文で考えます。

私は傘を買った

これは三つの文節に分けられるのでした。

私は||傘を||買った

これはKNPで構文解析すると

私は──┐ 
傘を──┤ 
      買った

となります。名詞同士をつないだ場合と異なり、ノード「私」とノード「傘」はノード「買った」を介してつながっています。

私-買った-傘

で、このとき問題になるのが「向き」です。「私」から「買った」にエッジが伸びるのでしょうか?それとも「買った」から「私」にエッジが伸びるのでしょうか?そもそも向きは不要でしょうか?

フランス革命は18世紀にフランスで起こった市民革命

この分をKNPで構文解析すると

  フランス──┐     
            革命は──┐ 
18世紀に──┐   │ 
フランスで──┤   │ 
          起こった──┤ 
              市民──┤ 
                      革命

となります。一般的な知識として、「市民革命」は「フランス革命」の上位概念にあたります。ということは、エッジの向きとしては

(上位概念)→(下位概念)

となるのが自然です。ところが、単純に係り受けの方向だけで考えると下位概念→上位概念という向きになってしまいます。「私は傘を買った」を文節で関係づけるときに向きを考えるとややこしくなってしまうので、結論としては無向グラフで考える方がやりやすそうです。上記のフランス革命の例文をざっくりグラフにするとこんな感じでしょうか。

フランス革命-市民革命-起こった-18世紀
               ┗-フランス

こうすると、「フランス革命」と「市民革命」がつながり、「18世紀」と「フランス」が「起こった」を介してつながるグラフになりました。なんとなく釈然としない気持ちが残らないではないですが、こうしておけば、「市民革命」を中心として「名誉革命」につながることもできますし、「フランス」を中心として「百年戦争」とつながることもできます。また、「18世紀」「フランス」「市民革命」でたどっていくと「フランス革命」に行き着くこともできるので、こちらの方が知識の構造化としても筋が良さそうです。
また、文節でノードを作れば、自動詞や形容詞の問題も解決できます。

まとめると、

構文解析の結果をグラフにするにあたっては

  • 文節をノードとし
  • 無向グラフとする

の2点に留意すると、ひとまずは筋の良さそうな構造化ができそうだ、という結論になりました。自分としては簡単だと思っていた方法が、実は余計面倒なことを引き起こす、というのはよくあることです。
当初は有向グラフにする予定でした。というのも、上記のような「上位概念」「下位概念」の階層構造をはっきりさせたかったから。でも、よくよく考えたら、上位概念にはエッジが集まりやすいし、ものによっては何を上位概念とするか、もあいまいだったりするので、とりあえず無向グラフで考えてもそれほど問題ないのかもしれません。

CabochaかKNPか、それが問題だ

巷にはいろいろな係り受け解析器がありますが、特に代表的なものがCabochaとKNPです。

CaoboCha: Yet Another Japanese Dependency Structure Analyzer

KNP - KUROHASHI-KAWAHARA LAB

果たしてどちらが良いでしょうか?
個人的にはKNPの方が自分の目的にかなっていると思っています。その理由を、CabochaとKNPの結果を引き比べて見ていきます。
※いずれもWindowsにインストールしたCabochaもしくはKNPをコマンドプロンプトから起動して解析しています。

解析に使用するのは次の例文です。

私は傘を買い、家に帰った。

ちょっと意地悪な例文です。というのも、「家に帰った」の部分の主語が明示されておらず、照応解析が必要になるためです。長めの日本語の文章にはこのような省略が多く、照応解析が必要になるケースが多くなります。さて、この例文の場合、どのように解析されるでしょうか。

Cabochaの場合

  私は-------D
    傘を-D   |
    買い、---D
        家に-D
        帰った

これ、よくよく見ると主語の「私は」が係っている先が「帰った」のみで、「買い」には係っていません。"-f1"オプションをつけて結果を見ると

* 0 4D 0/1 -2.366861
私      名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
* 1 2D 0/1 2.549938
傘      名詞,固有名詞,一般,*,*,*,傘,カサ,カサ
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
* 2 4D 0/0 -2.366861
買い    動詞,自立,*,*,五段・ワ行促音便,連用形,買う,カイ,カイ
、      記号,読点,*,*,*,*,、,、,、
* 3 4D 0/1 -2.366861
家      名詞,一般,*,*,*,*,家,イエ,イエ
に      助詞,格助詞,一般,*,*,*,に,ニ,ニ
* 4 -1D 0/1 0.000000
帰っ    動詞,自立,*,*,五段・ラ行,連用タ接続,帰る,カエッ,カエッ
た      助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。      記号,句点,*,*,*,*,。,。,。

文節番号0「私は」の係り先は文節番号4「帰った」になっています。本来、「私は」から「買い」にも係っていないといけません。このままでは使いづらそうです。
ちなみに、上記のCabochaの解析結果の見方は

qiita.com

こちらをご参照ください。

KNPの場合

        私は──┐ 
傘を──┐   │ 
     買い、<P>─┤ 
家に──┐   │ 
   帰った。<P>─PARA

CabochaにはなかったPやPARAという文字が出てきました。これは係り先が並列してある場合に表示されるもののようです。
では、今度は"-simple"と"-anaphora"の二つのオプションをつけて解析してみましょう。
※"-anaphora"は照応解析をオンにするオプションです。

* 4D <体言><係:未格>
+ 4D <体言><係:未格><EID:0>
私 わたし 私 名詞 6 普通名詞 1 * 0 * 0 "代表表記:私/わたし 漢字読み:訓 カテゴリ:人"
は は は 助詞 9 副助詞 2 * 0 * 0 NIL
* 2D <体言><係:ヲ格>
+ 2D <体言><係:ヲ格><EID:1>
傘 かさ 傘 名詞 6 普通名詞 1 * 0 * 0 "代表表記:傘/かさ 漢字読み:訓 カテゴリ:人工物-その他 ドメイン:家庭・暮らし"
を を を 助詞 9 格助詞 1 * 0 * 0 NIL
* 4P <用言:動><係:連用>
+ 4P <用言:動><係:連用><EID:2><述語項構造:買う/かう:動9:ヲ/C/傘/1;ガ/N/私/0>
買い かい 買う 動詞 2 * 0 子音動詞ワ行 12 基本連用形 8 "代表表記:買う/かう ドメイン:家庭・暮らし;ビジネス 反義:動詞:売る/うる"
、 、 、 特殊 1 読点 2 * 0 * 0 NIL
* 4D <体言><係:ニ格>
+ 4D <体言><係:ニ格><EID:3><述語項構造:家/いえ:名1:ノ/O/私/0>
家 いえ 家 名詞 6 普通名詞 1 * 0 * 0 "代表表記:家/いえ 漢字読み:訓 カテゴリ:組織・団体;場所-施設 ドメイン:家庭・暮らし"
に に に 助詞 9 格助詞 1 * 0 * 0 NIL
* -1D <用言:動><係:文末>
+ -1D <用言:動><係:文末><EID:4><述語項構造:帰る/かえる:動1:ニ/C/家/3;ガ/N/私/0>
帰った かえった 帰る 動詞 2 * 0 子音動詞ラ行 10 タ形 10 "代表表記:帰る/かえる 自他動詞:他:帰す/かえす 反義:動詞:行く/いく;動詞:来る/くる"
。 。 。 特殊 1 句点 1 * 0 * 0 NIL

オゥフ。。。
※ちなみに"-simple"オプションをつけないともっといろんな情報がでてきます。

行頭が"*"の行が文節の始まり、"+"の行が形態素の始まりだそうです。最初の文節「私は」は文節番号4「帰った」に係っていますから、そこだけみるとCabochaの結果と変わりません。が、最後の方に"述語項構造"というのがあるのに気付いたでしょうか。

* -1D <用言:動><係:文末>
+ -1D <用言:動><係:文末><EID:4><述語項構造:帰る/かえる:動1:ニ/C/家/3;ガ/N/私/0>
帰った かえった 帰る 動詞 2 * 0 子音動詞ラ行 10 タ形 10 "代表表記:帰る/かえる 自他動詞:他:帰す/かえす 反義:動詞:行く/いく;動詞:来る/くる"

文節番号4「帰った」は、ニ格として文節番号3の「家」を、ガ格として文節番号0の「私」をとる、と言っているのです。つまり、「家に帰った」において省略されている「私は」が、KNPでは照応解析を使って露わになった、ということなのです。

こいつぁすげえや!!

こういう理由があって、個人的にはKNPを推したいところなのです。
なのですが。
残念ながら、KNPは若干処理が遅いのが玉に瑕なのです。
上記の「私は傘を買い、家に帰った。」という短い文。Cabochaではenterを押した瞬間に結果が返ってくるのですが、KNPはちょっぴり気になるような「カクッ」とした間があります。そのあたりはまぁ、精度やらなんやらとトレードオフですし。。。

KNPの結果の見方は下記を参考にしてください。

qiita.com

参考までに「フランス革命は18世紀にフランスで起こった市民革命」の詳細を表示すると

* 4D <体言><係:未格>
+ 1D <係:文節内><体言><EID:5>
フランス ふらんす フランス 名詞 6 地名 4 * 0 * 0 "代表表記:フランス/ふらんす 地名:国"
+ 6D <体言><係:未格><EID:6>
革命 かくめい 革命 名詞 6 普通名詞 1 * 0 * 0 "代表表記:革命/かくめい カテゴリ:抽象物 ドメイン:政治"
は は は 助詞 9 副助詞 2 * 0 * 0 NIL
* 3D <体言><係:ニ格>
+ 4D <体言><係:ニ格><EID:7>
18 いちはち 18 名詞 6 数詞 7 * 0 * 0 "カテゴリ:数量 疑似代表表記 代表表記:18/いちはち"
世紀 せいき 世紀 接尾辞 14 名詞性名詞助数辞 3 * 0 * 0 "代表表記:世紀/せいき 準内容語 カテゴリ:時間"
に に に 助詞 9 格助詞 1 * 0 * 0 NIL
* 3D <体言><係:デ格>
+ 4D <体言><係:デ格><EID:5>
フランス ふらんす フランス 名詞 6 地名 4 * 0 * 0 "代表表記:フランス/ふらんす 地名:国"
で で で 助詞 9 格助詞 1 * 0 * 0 NIL
* 4D <用言:動><係:連格>
+ 6D <用言:動><係:連格><EID:8><述語項構造:起こる/おこる:動1:ガ2/N/市民革命/10;時間/C/18世紀/7;デ/C/フランス/5;ガ/O/フランス革命/6>
起こった おこった 起こる 動詞 2 * 0 子音動詞ラ行 10 タ形 10 "代表表記:起こる/おこる 自他動詞:他:起こす/おこす"
* -1D <体言><用言:判>
+ 6D <係:文節内><体言><EID:9>
市民 しみん 市民 名詞 6 普通名詞 1 * 0 * 0 "代表表記:市民/しみん カテゴリ:人 ドメイン:政治"
+ -1D <体言><用言:判><EID:10>
革命 かくめい 革命 名詞 6 普通名詞 1 * 0 * 0 "代表表記:革命/かくめい カテゴリ:抽象物 ドメイン:政治"

となります。ここでは「起こった」に述語項構造が入っており、「市民革命」と「フランス革命」がガ格で入っているのがわかります。加えて、「18世紀」は「時間」としてとらえられており、かなり使い勝手の良い結果になっています。つまり、この「述語項構造」だけからでもかなり多くの係り受けの情報が取れる、というわけです。
ただし、

フランス革命は市民革命

というような、「用言」が含まれていない文では

* 1D <体言><係:未格>
+ 1D <係:文節内><体言><EID:20>
フランス ふらんす フランス 名詞 6 地名 4 * 0 * 0 "代表表記:フランス/ふらんす 地名:国"
+ 3D <体言><係:未格><EID:6>
革命 かくめい 革命 名詞 6 普通名詞 1 * 0 * 0 "代表表記:革命/かくめい カテゴリ:抽象物 ドメイン:政治"
は は は 助詞 9 副助詞 2 * 0 * 0 NIL
* -1D <体言><用言:判>
+ 3D <係:文節内><体言><EID:21>
市民 しみん 市民 名詞 6 普通名詞 1 * 0 * 0 "代表表記:市民/しみん カテゴリ:人 ドメイン:政治"
+ -1D <体言><用言:判><EID:10>
革命 かくめい 革命 名詞 6 普通名詞 1 * 0 * 0 "代表表記:革命/かくめい カテゴリ:抽象物 ドメイン:政治"

このように述語項構造が出力されないので、このような場合には単純に文節項の係り先文節番号を見るのが良いでしょう。

結論

KNPすごい。
※ちょっと遅いのは我慢するよ!