言語処理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