1: 2016-11-11 (金) 15:08:57 njf |
現: 2016-11-27 (日) 21:05:42 njf |
- | CaboChaはどの単語がどの単語を修飾しているかなど、係り受け関係を解析するツール。 | + | *CaboChaとは [#o6e77da9] |
| + | |
| + | CaboChaはどの単語がどの単語を修飾しているかなど、日本語の係り受け関係を解析するツール。 |
| 詳細は公式ページ[[https://taku910.github.io/cabocha/:https://taku910.github.io/cabocha/]]を参照のこと。 | | 詳細は公式ページ[[https://taku910.github.io/cabocha/:https://taku910.github.io/cabocha/]]を参照のこと。 |
| + | |
| + | 例えば |
| + | |
| + | echo "これは私のもっている赤いペンです"|cabocha |
| + | |
| + | とすると、 |
| + | これは-----D |
| + | 私の-D | |
| + | もっている-D |
| + | 赤いペンです |
| + | EOS |
| + | |
| + | とツリー出力されます。これはわかりやすいようで曖昧な感じの表現です。より厳密な表示をさせるため「-f1」オプションをつけて実行しなおすと以下のようになります。 |
| + | |
| + | echo "これは私のもっている赤いペンです"|cabocha -f1 |
| + | |
| + | * 0 3D 0/1 -2.060711 |
| + | これ 名詞,代名詞,一般,*,*,*,これ,コレ,コレ |
| + | は 助詞,係助詞,*,*,*,*,は,ハ,ワ |
| + | * 1 2D 0/1 2.120296 |
| + | 私 名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ |
| + | の 助詞,格助詞,一般,*,*,*,の,ノ,ノ |
| + | * 2 3D 0/2 -2.060711 |
| + | もっ 動詞,自立,*,*,五段・タ行,連用タ接続,もつ,モッ,モッ |
| + | て 助詞,接続助詞,*,*,*,*,て,テ,テ |
| + | いる 動詞,非自立,*,*,一段,基本形,いる,イル,イル |
| + | * 3 -1D 1/2 0.000000 |
| + | 赤い 名詞,一般,*,*,*,*,赤井,アカイ,アカイ |
| + | ペン 名詞,一般,*,*,*,*,ペン,ペン,ペン |
| + | です 助動詞,*,*,*,特殊・デス,基本形,です,デス,デス |
| + | EOS |
| + | |
| + | アスタリスクから始まる行がCaboChaの解析結果で、それ以外は形態素解析の結果です。ここではMecabを使っているので、その結果となっています。 |
| + | |
| + | 一つ目の |
| + | * 0 3D 0/1 -2.060711 |
| + | を例にすると、各データの意味は以下の通りです。 |
| + | |
| + | -最初の0は文節の通番。文頭なので0 |
| + | -3Dは数字の部分がその文節がかかっている通番。ここでは通番3の「赤いペンです」にかかっている。かかり先がなければ-1。よって文末は常に-1。Dの意味は資料がなく不明 |
| + | -0/1の主辞(文節の中心となる単語)と機能語(助詞など)の位置を示している。この例では「私」が主辞で「の」が機能語 |
| + | -次の小数値はかかりやすさの度合い |
| + | |
| + | CaboChaは文を文節、つまり形態素の集まりに分解し解析するので、CaboChaのそれぞれの結果には複数の形態素が含まれます。アスタリスクから始まる行の続きの行がその形態素です。つまりこの通番0の結果には「これ」「は」の二つの形態素が含まれます。 |
| + | |
| + | 実際によく使うのは最初の通番と二番目のかかり先です。主辞/機能語は公式ページによると廃止も検討されています。かかりやすさの度合いは実際にどの程度精度に関わっているかは調査中だそうです。 |
| + | |
| + | よって上の例をまとめると、 |
| + | -「これは」は「赤いペンです」にかかっている |
| + | -「私の」は「もっている」にかかっている |
| + | -「もっている」は「赤いペンです」にかかっている |
| + | -「赤いペンです」は文末なのでかかり先なし |
| + | |
| + | となります。 |
| + | |
| + | コマンドライン引数で形態素解析のエンジンにパラメーターを渡すことも可能です。 |
| + | |
| + | echo "おいしいチーズケーキをもらった"|cabocha -f1 |
| + | |
| + | の結果は |
| + | |
| + | * 0 1D 0/0 1.755362 |
| + | おいしい 形容詞,自立,*,*,形容詞・イ段,基本形,おいしい,オイシイ,オイシイ |
| + | * 1 2D 1/2 1.755362 |
| + | チーズ 名詞,一般,*,*,*,*,チーズ,チーズ,チーズ |
| + | ケーキ 名詞,一般,*,*,*,*,ケーキ,ケーキ,ケーキ |
| + | を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ |
| + | * 2 -1D 0/1 0.000000 |
| + | もらっ 動詞,自立,*,*,五段・ワ行促音便,連用タ接続,もらう,モラッ,モラッ |
| + | た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ |
| + | EOS |
| + | |
| + | となって「チーズケーキ」が認識されないですが、例えば「 /usr/local/lib/mecab/dic/mecab-ipadic-neologd/」にmecab-ipadic-NEologdの辞書をインストールしておけば、 |
| + | |
| + | echo "おいしいチーズケーキをもらった"|cabocha -f1 -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd/ |
| + | |
| + | という風にmecabの「-d」オプションを使うことで、 |
| + | |
| + | * 0 1D 0/0 1.656278 |
| + | おいしい 形容詞,自立,*,*,形容詞・イ段,基本形,おいしい,オイシイ,オイシイ |
| + | * 1 2D 0/1 1.656278 |
| + | チーズケーキ 名詞,固有名詞,一般,*,*,*,チーズケーキ,チーズケーキ,チーズケーキ |
| + | を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ |
| + | * 2 -1D 0/1 0.000000 |
| + | もらっ 動詞,自立,*,*,五段・ワ行促音便,連用タ接続,もらう,モラッ,モラッ |
| + | た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ |
| + | EOS |
| + | |
| + | と出力されます。 |
| + | |
| + | 他にもいろいろなコマンドラインオプションがありますが、ここでは使わないので詳細は公式ページを参照してください。 |
| + | |
| + | *Pythonから使う [#o198f23f] |
| + | |
| + | CaboChaはpythonから使うこともできます。インストール方法などは他にもたくさんの資料があるのでそちらにゆずるとして、使い方は、まずインポートして |
| + | import CaboCha |
| + | |
| + | 次にパーサーを取得します。このとき前節のコマンドラインオプションと同様に、辞書を指定することもできます。 |
| + | |
| + | c = CaboCha.Parser("-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd/") |
| + | |
| + | あとはこのパーサーでパースします。 |
| + | parsed = c.parse("これは私のもっている赤いペンです") |
| + | |
| + | ツリー表示なども可能です。 |
| + | |
| + | parsed = c.parse("これは私のもっている赤いペンです") |
| + | print parsed.toString(CaboCha.FORMAT_TREE) |
| + | これは-----D |
| + | 私の-D | |
| + | もっている-D |
| + | 赤いペンです |
| + | EOS |
| + | |
| + | 注意しないといけないのは、パーサーは内部的にデータの保持はしてくれないことです。つまり |
| + | |
| + | parsed = c.parse("これは私のもっている赤いペンです") |
| + | parsed2 = c.parse("おいしいチーズケーキをもらった") |
| + | print parsed.toString(CaboCha.FORMAT_TREE) |
| + | |
| + | とすると結果は |
| + | おいしい-D |
| + | チーズケーキを-D |
| + | もらった |
| + | EOS |
| + | |
| + | となり変数parsed2に結果を格納したつもりでも、parsedの方も書きかわってしまいます。そのため、パースしたその結果を保持したければ、自分で他の変数に結果を移さなければなりません。 |
| + | |
| + | そのためにはパース結果からデータを取り出す必要があります。 |
| + | パーサーが返す結果には次のメソッドがあります。 |
| + | |
| + | まず文節のデータに関しては |
| + | |
| + | |メソッド名|引数|概要| |
| + | |chunk|整数|引数番目の文節のデータを取得する| |
| + | |chunk_size|なし|文節の個数を取得する| |
| + | |
| + | 一方、形態素解析のデータに関しては |
| + | |
| + | |メソッド名|引数|概要| |
| + | |token|整数|引数番目の形態素のデータを取得する| |
| + | |token_size|なし|形態素の個数を取得する| |
| + | |
| + | というメソッドがあります。 |
| + | |
| + | よって文節のデータを全て取り出すには、先ほどパースした「parsed」に対して以下のようにします。 |
| + | |
| + | for i in range(parsed.chunk_size()): |
| + | chunk = parsed.chunk(i) |
| + | |
| + | このとき取り出した一つ一つのデータ(上の例ではchunk)には以下のプロパティがあります。 |
| + | |
| + | |プロパティ名|概要| |
| + | |score|かかりやすさの度合い| |
| + | |link|文節がかかっている通番| |
| + | |token_size|含まれる形態素の数| |
| + | |token_pos|含まれる形態素の先頭の場所| |
| + | |head_pos|主辞の場所| |
| + | |func_pos|機能語の場所| |
| + | |
| + | ほとんどのプロパティは前節のコマンドラインで実行した例で解説しているので、分からないところは戻って確認してください。token_posは例えば |
| + | |
| + | first_token = parsed.token(chunk.token_pos) |
| + | |
| + | とするとその文節の最初の形態素が取得できます。 |
| + | |
| + | 一方、tokenつまり形態素解析の結果についてはMecabのpythonバインディングと全く同じ格納のされかたをされています。詳細は[[自然言語解析/MeCabをpythonで使う]]を参照してください。 |
| + | |
| + | 例えば |
| + | |
| + | print token.surface |
| + | |
| + | とすれば元の単語が表示されます。 |
| + | よって |
| + | |
| + | for i in range(parsed.chunk_size()): |
| + | chunk = tree.chunk(i) |
| + | for j in range(chunk.token_size): |
| + | token = parsed.token(chunk.token_pos + j) |
| + | print token.surface, |
| + | |
| + | とすると、元の文章が空白区切りで出力されます |
| + | |
| + | これらのことから、Cabochaの結果をpythonで保持するには、次のようなクラスがあれば良いことになります。 |
| + | |
| + | 各々の文節(chunk)について |
| + | |
| + | class CabochaChunk: |
| + | __ATTRS = ("score","link","token_pos","token_size","head_pos","func_pos") |
| + | def __init__(self,chunk): |
| + | for attr in self.__ATTRS: |
| + | setattr(self, attr, getattr(chunk, attr)) |
| + | |
| + | を定義し、 |
| + | cabochaCunk = CabochaChunk(chunk) |
| + | とすれば文節のデータを保持できます。 |
| + | あとはこれと形態素のデータを配列で保持するクラスを作れば、CaboChaの結果が保持できます。 |
| + | class CabochaResult: |
| + | def __init__(self,tree): |
| + | self.reSetPrm(tree) |
| + | def reSetPrm(self,tree): |
| + | self.chunks = [] |
| + | self.tokens = [] |
| + | for i in range(tree.chunk_size()): |
| + | chunk = tree.chunk(i) |
| + | cc = CabochaChunk(chunk) |
| + | self.chunks.append(cc) |
| + | for i in range(tree.token_size()): |
| + | token = tree.token(i) |
| + | mr = MecabResult(oken.surface,token.feature) |
| + | self.tokens.append(mr) |
| + | |
| + | ここでMecabResultはCabochaChunkと同様にMecabの形態素解析の結果を保持するクラスです。 |
| + | |
| + | *実際の利用例 [#he8d5f70] |
| + | |
| + | 実際には例えば飲食店の口コミの解析で使ってみました。 |
| + | |
| + | まず形態素の頻度解析を行い、その上位の名詞について係り受け解析を行い、かかっている語の中から形容詞を抜き出してみました。 |
| + | |
| + | あるラーメン店でそれを行ってみた結果が以下の通りです。 |
| + | |
| + | ラーメン :やさしい :良い :なく :分厚い :無かっ :嬉しい :安い :硬い :おいしい :優しい :うまい :潔い :旨い |
| + | スープ :熱い :おいしい :いい :茶色い :こい :薄い :高い :良い :うまい :柔かい :甘い |
| + | 麺 :柔い :固い :面白い :淡い :おいしい :硬い :柔かい :堅い |
| + | 卵 :無し :硬い :潔い :なく :良い |
| + | テーブル :多い |
| + | 豚骨 :薄い :おいしい :いい |
| + | |
| + | 上の結果から、とんこつで茶色いスープ、固めの麺でトッピングは玉子、値段は安めで優しい味、テーブル席が多めの店かなと予想できます。 |
| + | |
| + | 実際の口コミを読むと、麺は「固くなく」という表現が多く、実は柔らかいことがわかりました。上の結果でも「柔らかい」もあり微妙なところです。他の部分はあっていました。 |
| + | |
| + | 否定語などの扱いをどうするかをちゃんとすれば、ある程度印象のようなものが解析できそうです。 |
| + | また料理は形容詞以外、例えば名詞(例:ボリュームがある)や副詞(例:こってり)、比喩表現などで説明することも多く、そういったものも解析できれば精度が上げられるかもしれません。 |
| + | |
| + | 他の問題点として同じラーメンでも複数の主力メニューがあるとその印象が混じってしまう可能性があります。 |
| + | 対象となるのが何かを判断するには、各々の文がどういう構造かではなく、全体の文脈が必要となるので、この方法ではこの問題を解決するのは難しそうです。 |
| + | |
| + | 係り受けや頻度解析からさらに精度を上げるには、プログラムに何らかの形で「意味」を理解させたりすることが必要かも知れません。 |