【cabochaで】引き続き修造の「人生を強く生きる83の言葉」の頻出文節を調べてみる。【形態素解析】




スクリーンショット 2015-02-17 5.23.00
かぼちゃの煮つけフリー画像がなさすぎて諦めた。

こんにちは。

先日のmecab記事が個人的に会心だったのにあまり当たらず世の中そんなものだと感じ入っているvsannaです。修造人気にあやかれなかった。

先日の記事はこちら => 【natto・mecabで】5分で形態素分析に入門して、修造の「人生を強く生きる83の言葉」の頻出語を調べてみる。【形態素解析】

今回はプログラミング入門実践編シリーズの一環である形態素分析の続編として、文節で解析できるcabochaという形態素解析エンジンのご紹介です。
なお使用言語はRubyです。

今後は食べ物系ブログとして名をはせていく所存。

今回やることと最初に感想

今回も前回と同様、「修造の『人生を強く生きる83の言葉』の頻出語を調べてみる」を進めていきます。
ただし今回は品詞ではなく文節単位で。

最初にやってみた感想をまとめてしまうと、夢は広がる。楽しい。でも修造でやったのは失敗だった。
これにつきます。では早速本編へ。

使うものはmecabと(CRF++と)cabocha

今回の下準備は少し面倒。私のようなプログラミング入門者の場合、1時間は準備に時間かかりそうだと思っておきましょう。

mecabの用意

まずはmecabをmacにインストール。
こちら => Macにmecabインストールの「本来入っている辞書を追加」まで進めてみてください。
windowsの方はggってみて下さい!

もしくはMac買っちゃいましょう★

なお今回natto(gem)は不要です。
ターミナルで

$mecab -v

として、

mecab of 0.996

とうまいこと表示されればOK。

CRF++の用意

CRF++とは形態素解析のためのアルゴリズムとのこと。詳しいことはわからないですが必要ということなので入れておきます。
なお、このインストール過程が初心者にはかなり奇妙に映りますのでターミナルのコマンドも含めて丁寧に見ていきます。

# urlからtar形式で圧縮されたファイルをcurlコマンドでダウンロード
$ curl http://crfpp.googlecode.com/files/CRF%2B%2B-0.58.tar.gz
# tarコマンド + zvxfオプションでtar.gz形式のファイルを解凍(tar, gzipという圧縮タイプがあるらしい。初めて知った。)
$ tar zvxf  CRF++-0.58.tar.gz
# 解凍したファイルの中にcdコマンドで移動
$ cd CRF++-0.58
# これがわかりにくい。 shファイル(いわゆるシェルコマンド!)のconfigure.shを実行している。これでMakefileというソースファイルが作成されているらしい。
$ ./configure
# makeコマンドで上記をコンパイル。この辺り詳しくは「make ./configure」でggrましょう。
$ make 
# コンパイルしたファイルを実行してインストール!sudoをつけてスーパーユーザーとして実行します。
$ sudo make install
# ホームディレクトリに戻っておきます。
$ cd ~

初見でちょっとこわいコマンドだらけですね本当にありがとうございました。
「ほ、ほう…」とひるんだあなた。大丈夫、誰しもひるみます。少なくとも私は一回ベッドに逃げ込むくらいにはひるみました。。

ここで登場したコマンドの解説urlを最後にまとめておきます。

cabochaの用意

続いてcabochaのインストールを進めます。
基本はCRF++のインストールと同様ですが、最後にcabochaの起ち上げをしてみます。

$ curl http://cabocha.googlecode.com/files/cabocha-0.67.tar.bz2
$ tar xjvf cabocha-0.67.tar.bz
$ cd cabocha-0.67
$ ./configure --with-charset=UTF8 --with-posset=IPA
$ make
$ sudo make install

ここまででインストール終了。

ターミナルで

$cabocha -v

として、

cabocha of 0.67

といった感じでバージョン表記されればOK。

早速ちょっと遊んでみる

ではさっそく遊んでみましょう。今回の肝は文節単位で解析できることです。楽しみですね。

# gemの読み込み。今回はcabochaだけでOK。なおcabochaインストール時にruby用ライブラリがセットで入ってきてます。
require 'CaboCha'
# 例文の修飾関係が非常に複雑。ですが...
sentence = "太郎はこの本を二郎を見た女性に渡した。"

# cabocha起動!
c = CaboCha::Parser.new;
# サクッと分析したいときはparserインスタンスに直接原文を放り込む(フィードする)。
puts c.parseToString(sentence)

# 基本はtreeインスタンスをいう形にしてからいろいろ作業していきます。
tree =  c.parse(sentence)

puts tree.toString(CaboCha::FORMAT_TREE);
puts tree.toString(CaboCha::FORMAT_LATTICE);

をcabocha_otameshi.rbといった名前で保存し、

$ ruby cabocha_otameshi.rb

で実行。

  太郎は-----------D
      この-D       |
        本を---D   |
        二郎を-D   |
            見た-D |
            女性に-D
            渡した。
EOS
  太郎は-----------D
      この-D       |
        本を---D   |
        二郎を-D   |
            見た-D |
            女性に-D
            渡した。
EOS
* 0 6D 0/1 -2.457381
太郎    名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー
は    助詞,係助詞,*,*,*,*,は,ハ,ワ
* 1 2D 0/0 1.509507
この    連体詞,*,*,*,*,*,この,コノ,コノ
* 2 4D 0/1 0.091699
本    名詞,一般,*,*,*,*,本,ホン,ホン
を    助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
* 3 4D 1/2 2.359707
二    名詞,数,*,*,*,*,二,ニ,ニ
郎    名詞,一般,*,*,*,*,郎,ロウ,ロー
を    助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
* 4 5D 0/1 1.416783
見    動詞,自立,*,*,一段,連用形,見る,ミ,ミ
た    助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
* 5 6D 0/1 -2.457381
女性    名詞,一般,*,*,*,*,女性,ジョセイ,ジョセイ
に    助詞,格助詞,一般,*,*,*,に,ニ,ニ
* 6 -1D 0/1 0.000000
渡し    動詞,自立,*,*,五段・サ行,連用形,渡す,ワタシ,ワタシ
た    助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。    記号,句点,*,*,*,*,。,。,。
EOS

と表示されればOK!

文節単位で解析されているし、さらに文節のかかり方も解析されているではありませんか。
これは夢が広がる。。

というわけで修造83の名言を解析してみる。

いよいよ今回の本題。修造を分節区切りにしてみる。

# 今回使うライブラリを読んでおく。
require 'CaboCha'
require 'pp'
require 'csv'

# 下準備
syuzo_words = []

# cabocha起動
c = CaboCha::Parser.new

CSV.open('syuzo.csv', 'r') do |reader|
    # 読み込んだcsvファイルの各行を順次処理する。
    reader.each do |row|

        # ここでcabochaを介してパース!(解析)
        tree = c.parse(row[0])
        # 横着して、改行で分割して配列に。文節ごとに改行されるので。
        # なお、cabochaの出力がascii-8bitなるエンコードなのでutf-8に変更してます。
        # 更に、不要な - D |を削除。これってまとめて削除できないのかな... 
        s = tree.toString(CaboCha::FORMAT_TREE).force_encoding("utf-8").gsub(" ","").gsub("-","").gsub("|","").gsub("D","").split("\n")
        syuzo_words += s
    end
end

words_and_count = []

# 文節分解した結果を集計
# #uniqで重複をなくしつつ、
# #map処理で集計した文節(word)と、
# それが何回カウントされているか(syuzo_words.grep(word).count)を作り出している。
syuzo_words.uniq.map do |word|  
    words_and_count[words_and_count.size] = ["#{word}", "#{syuzo_words.grep(word).count}"] if word
end
# ここまででこんな感じ
# words_and_count = 
#["ナイスボレー、", "1"],
#["修造!", "1"],
#["EOS", "82"],
#["緊張してきた。", "1"],...]

# 最後にsort_byで並び替えて更に降順に。
# word_and_count[1]とは["ナイス", "2"]のindex1つまり、"2"の値を指しており、その値を元に並び替え。
pp words_and_count.sort_by { |word_and_count| word_and_count[1].to_i }.reverse

よし完成。
syuzo_cabocha.rbとでも名づけてすぐさま実行。

$ ruby syuzo_cabocha.rb

で、出てきた結果がこちら。

[["EOS", "82"],
["ことは", "4"],
["自分の", "3"],
["僕は", "3"],
["ある", "3"],
["自分を", "3"],
["心の", "3"],
["今日から", "2"],
["それが", "2"],
["でも", "2"],
["かならず", "2"],
["大丈夫、", "2"],
["君は", "2"],
["心で", "2"],
["お前は", "2"],
["心に", "2"],
["ある。", "2"],
["中に", "2"],
["その", "2"],
["時は", "2"],
["悪い", "2"],
["大切なのは", "2"],
["時も", "2"],
["笑顔は", "2"],
["な", "2"],
["見てみろよ。", "2"],
["力が", "2"],
["影には", "1"],
["とき幸せだったら", "1"],
///中略///
["崖っぷちありがとう!最高だ!", "1"],
["OK!ナイストライ!", "1"],
["行く。", "1"],
["勝ちに", "1"],
["勝てない。", "1"],
["尽くすだけでは", "1"],
["ベストを", "1"],
["僕だ!", "1"],
["れんこん畑のように", "1"],
["粘っこく生きろよ", "1"],
["曇って", "1"],
["ところが", "1"],
["神経質な", "1"],
["そして", "1"],
["明るいだけ。", "1"],
["ただ", "1"],
["見えない", "1"],
["自分だ!", "1"],
["創るのは", "1"],
["富士山はな、", "1"],
["あるがままに", "1"],
["わがままではなく、", "1"],
["思いだせ!", "1"],
["うってきたんだ、", "1"],
["何万球", "1"],
["喜ぼうとしているよ!", "1"],
["震えて", "1"],
["ブルブル", "1"],
["今", "1"],
["脳、", "1"],
["ー!俺の", "1"],
["やった", "1"],
["よっしゃー!", "1"],
["緊張してきた。", "1"],
["見るんだ", "1"],
["修造!", "1"],
["ナイスボレー、", "1"]

うーん。前回の品詞分解の方が修造特徴は捉えられていておもしろかったかも。
恐らく83の例文じゃまだまだ足りなすぎるんだろうな…
ていうか何やってんだろうな…

となんとなく意味のない時間を過ごしているのではと気付き始めたので終わりにします。

もっと出来そうなこと

  • bot
    • むしろ文節解析の本領はこちらなのかも。
    • 係り受けのパターンを分析して自動応答を実装できるらしい。
    • 詳しくは最後の参考urlをご参考下さい。
  • エディターで自身の文体傾向分析
    • ESやレポートなどでも使えそう
    • 自分の文体の癖を解析できたらよさげ
  • 修辞学のレポート課題を自動作成
    • ツリーを手書きしているそこのアナタ。自動でレポート出せそうです。

ちょっと詳しくないのであまり具体的なイメージが湧いてないですが、文節単位での解析ができると色々発想は広がります。

終わりに

さて今回も引き続き形態素分析のご紹介でした。cabochaへん。

前回もまとめで書きましたが、プログラミング入門者が実践編で色々動かしてみるお題としては面白い材料だと思います。
ぜひご自身のtweet傾向でも分析してみると面白いのではないでしょうか。

今回も最後までお読みいただきありがとうございました。
なにか間違えなどございましたらぜひコメント頂ければ幸いです。

はてぶやフォローをしてもらえるとうれしいです。よろしくです(´・ω・`)

参考1:参考url

参考2:他のメソッドご紹介

tree系

# treeインスタンス(Cabocha::Tree)を入手。
tree = c.parse("修造の魂の叫び") 

puts tree.toString(CaboCha::FORMAT_TREE) #=> 係り受け表示。
puts tree.toString(CaboCha::FORMAT_LATTICE) #=> 形態素分析っぽい

## treeのメソッド。puts tree.methodsで確認できます。
# set_sentence        #=> 原文をフィードする 
# sentence            #=> 原文を返す。    "修造の魂の叫び"
# sentence_size       #=> 原文のバイト数を返す。   21 (漢字や平仮名は3バイト)
# chunk(n)            #=> n番目の文節を返す。     #<CaboCha::Chunk:0x007fe2a994cdf0>
# token(n)            #=> n番目の品詞を返す。    #<CaboCha::Token:0x007fd0a18d8d48>
# empty               #=> 空かどうかをチェック    false
# clear               #=> セットした原文をクリア
# chunk_size          #=> 文節数を返す            3
# token_size          #=> 品詞数を返す            5
# size                #=> 品詞数を返す            5
# toString(const)     #=> 解析する。使い方は上記!

# === 以下不明なメソッド。===
# clear_chunk
# read
# charset
# set_charset
# posset
# set_posset
# output_layer
# set_output_layer

chunk系

# treeインスタンスを元に、先頭から1番目のchunkインスタンス(Cabocha::Chunk)を入手
chunk = tree.chunk(0) #「修造の」を取得。

puts chunk.head_pos      # 主辞の位置を返す            => 0
puts chunk.func_pos      # 機能語の位置を返す          => 1
puts chunk.token_size    # 品詞の数を返す              => 2
puts chunk.link          # かかっている文節の位置を返す => 1 (「魂の」にかかっている)
puts chunk.score         # かかりやすさ?という指標らしい => 1.183841586112976

## その他のchunkのメソッド puts tree.chunk(0).methodsで確認
# === この辺りよくわからん。===
# feature_list(1)    # SHS:修造
# feature_list_size  # 20
# token_pos          # 0
# additional_info
# feature_list_size
# feature_list

token系

# treeインスタンスを元に先頭から1番目のtokenインスタンス(Cabocha::Token)を入手
token = tree.token(0)

puts token.surface                # 表記を入手 => 修造
puts token.normalized_surface     # 同上。違い不明 => 修造
puts token.feature                # 特徴をカンマ区切りで表示 => 名詞,代名詞,一般,*,*,*,これ,コレ,コレ
puts token.feature_list(2)        # 上記#featureのリストから要素を取得 => 一般
puts token.chunk                  #  所属するchunkを返す => <CaboCha::Chunk:0x007fd60a01c538>(「修造の」にあたるchunkインスタンス)
puts token.feature_list_size      # featureの個数。 => 9

# その他のtokenのメソッド puts tree.token(0).methods!
# === この辺りよくわからん。===
# ne
# additional_info
# chunk


コメントを残す