言語処理100本ノック 2015をやってみた(第5章 その1 40,41)
スポンサーリンク
5章難しくてなかなか進まない。プログラムが長くなってしまうので小出しで行きます。
5章で使うCabochaについて
CabochaというSupport Vector Machines に基づく日本語係り受け解析器ということで、このモジュールに文章をぶち込むと、文節に分解しさらに、どの文節番号にかかっているかとか出力してくれます。聞いたこともないツールなので、私みたいな初心者は一度、以下をざっと見たほうが理解が早いです。
- Cabochaとは
CaoboCha: Yet Another Japanese Dependency Structure Analyzer
- Cabochaのインストール方法
CabochaのUbuntuへのインストール(Pythonでテスト) - "Diary" インターネットさんへの恩返し
- Cabochaの出力結果の解説。
準備
「neko.txt.cabocha」への保存。
#!/usr/bin/python # -*- coding: utf-8 -*- import CaboCha c = CaboCha.Parser() f_r = open('neko.txt','r') f_w = open('neko.txt.cabocha','a') line = f_r.readline() # 1行を文字列として読み込む(改行文字も含まれる) while line: if len(line)>1: //行に一文字以上ある場合 tree = c.parse(line[:-1]) f_w.write(tree.toString(CaboCha.FORMAT_LATTICE)) line = f_r.readline() f_r.close f_w.close
「f_w.write(tree.toString(CaboCha.FORMAT_LATTICE))」で1文を分解して以下の様な形にして「neko.txt.cabocha」へ書込。
* 1 2D 0/1 -0.764521 吾輩 名詞,代名詞,一般,*,*,*,吾輩,ワガハイ,ワガハイ は 助詞,係助詞,*,*,*,*,は,ハ,ワ * 2 -1D 0/2 0.000000 猫 名詞,一般,*,*,*,*,猫,ネコ,ネコ で 助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ ある 助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル 。 記号,句点,*,*,*,*,。,。,。 EOS
上記リンクに説明がありますがそれぞれの行は以下の意味を持つ
[1行目]
* 文節番号 係り先の文節番号(係り先なし:-1) 主辞の形態素番号/機能語の形態素番号 係り関係のスコア(大きい方が係りやすい)
[2-3行目]
表層形 品詞 品詞細分類1 品詞細分類2 品詞細分類3 活用形 活用型 原形 読み 発音
1-3行名で1文節。4-8で2文節目。「EOS」文章終わり。
40. 係り受け解析結果の読み込み(形態素)
オブジェクトのイメージ
【プログラム】
# coding: utf-8 import sys import re import json f = open('neko.txt.mecab','r') class Morph: def __init__(self,surface,base,pos,pos1): self.surface = surface self.base = base self.pos = pos self.pos1 = pos1 if __name__ == "__main__": f = open('neko.txt.cabocha','r') tmp1 = [] temp1 = {} no_sentence = 0 no_word = 0 temp1[no_sentence] = {} #2次元配列(行:文番号、列:単語番号)に形態素列クラスを入れる for i,line in enumerate(f): if "EOS" == line[0:3]: no_sentence += 1 no_word = 0 temp1[no_sentence] = {} #print "\n\n" #空白を除去したい if ("*" != line[0:1]) and ("EOS" != line[0:3]): # \tか,でスプリット tmp1 = filter(lambda w: len(w) > 0, re.split(r'\t|,', line)) if tmp1[2]!="空白": #0:表層形,1:品詞,2:品詞細分類1,3:品詞細分類2,4:品詞細分類3,5:活用形,6:活用型,7:原形,8:読み,9:発音 #print line[:-1] + " => " + str(no_sentence) + "文目の " + str(no_word) + " 文字目" #2次元配列に形態素列インスタンス格納 temp1[no_sentence][no_word] = Morph(tmp1[0],tmp1[7],tmp1[1],tmp1[2]) no_word += 1 #3文目の形態素列 for a in temp1[2]: print "表層形:" + temp1[2][a].surface + "\t原形:" + temp1[2][a].base + "\t品詞:" + temp1[2][a].pos + "\t品詞細分類1:" + temp1[2][a].pos1
【実行結果】
$ sudo python 40.py 表層形:名前 原形:名前 品詞:名詞 品詞細分類1:一般 表層形:は 原形:は 品詞:助詞 品詞細分類1:係助詞 表層形:まだ 原形:まだ 品詞:副詞 品詞細分類1:助詞類接続 表層形:無い 原形:無い 品詞:形容詞 品詞細分類1:自立 表層形:。 原形:。 品詞:記号 品詞細分類1:句点
41. 係り受け解析結果の読み込み(文節・係り受け)
オブジェクトのイメージ
【プログラム】
#coding: utf-8 import sys import re import json f = open('neko.txt.mecab','r') class Morph: def __init__(self,surface,base,pos,pos1): self.surface = surface self.base = base self.pos = pos self.pos1 = pos1 class Chunk: def __init__(self): self.morphs = [] self.dst = 0 self.srcs = [] if __name__ == "__main__": f = open('neko.txt.cabocha','r') tmp1 = [] tmp2 = [] temp1 = {} no_sentence = 0 no_bunsetsu = 0 temp1[no_sentence] = {} #2次元配列(行:文番号、列:単語番号)に形態素列クラスを入れる for i,line in enumerate(f): #1行名またはEOSの場合 if (i==0) or ("EOS" == line[0:3]): no_bunsetsu = 0 #EOSの場合 if "EOS" == line[0:3]: #srcs代入 for i in temp1[no_sentence]: if temp1[no_sentence][i].dst != "-1": temp1[no_sentence][int(temp1[no_sentence][i].dst)].srcs.append(i) no_sentence += 1 temp1[no_sentence] = {} #print "\n【文章番号 " + str(no_sentence) + "】" #文節情報 elif "*" == line[0:1]: #print "\n[文節番号 " + str(no_bunsetsu) + "]" tmp1 = filter(lambda w: len(w) > 0, re.split(r' ', line)) #インスタンスを作って文節情報のみ格納する temp1[no_sentence][no_bunsetsu] = Chunk() #かかり先インデックス番号を格納 temp1[no_sentence][no_bunsetsu].dst = tmp1[2].replace("D","") no_bunsetsu += 1 #単語 else: # "\t"か","でスプリット tmp1 = filter(lambda w: len(w) > 0, re.split(r'\t|,', line)) #if tmp1[2]!="空白": temp1[no_sentence][no_bunsetsu - 1].morphs.append(Morph(tmp1[0],tmp1[7],tmp1[1],tmp1[2])) #8文目の文節の文字列と係り先を表示 text = "" for a in temp1[7]: text += "文節No " + str(a) + " 「" for b in temp1[7][a].morphs: text += b.surface text += "」かかり先文節番号" + temp1[7][a].dst + "\n" print text
【実行結果】
$ sudo python 40.py 文節No 0 「この」かかり先文節番号1 文節No 1 「書生というのは」かかり先文節番号7 文節No 2 「時々」かかり先文節番号4 文節No 3 「我々を」かかり先文節番号4 文節No 4 「捕えて」かかり先文節番号5 文節No 5 「煮て」かかり先文節番号6 文節No 6 「食うという」かかり先文節番号7 文節No 7 「話である。」かかり先文節番号-1