KNPを用いた手法
KNPは地名や人名を正確に分類できるためWhen/Where抽出する時に役立ちますが、
係り受け解析は文節ではなくチャンクごとに分類するCaboChaの方が正確であることが多いです。
そのため、When/Whoの抽出にはKNPを、Why/What/Howの抽出にはCaboChaを使用することで、今までで最も正確に5W1Hを抽出することが可能になりました。
以下にソースコードを示します。
ソースコード
python import CaboCha from pyknp import KNP import re sentence = 'きょうの会見に合わせたかのように宮迫さんがユーチューバーとしてまーデビューされました' When = [] Where = [] What = [] Why = [] Who = [] How = [] c = CaboCha.Parser('-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd') words = [] poss = [] chunkId = [] toId = [] ID = -1 scores = [] howId = [] nani = ['は','が','を','の'] # Whatの後に続く助詞 naze =['だから', 'それで', 'そのため', 'このため', 'そこで', 'したがって', 'ゆえに', 'それゆえに','から','んで','ため']# Whyの後に続く接続助詞 tree = c.parse(sentence) knp = KNP(option='-tab') # Default is JUMAN++. If you use JUMAN, use KNP(jumanpp=False) result = knp.parse(sentence) for i in range(0, tree.size()): token = tree.token(i) # 単語を取得 word = token.surface words.append(word) # 文字をスタック # 品詞を取得 pos = token.feature.split(",") poss.append(pos) # 品詞をスタック #チャンクIDを取得 if token.chunk != None: ID += 1 chunkId.append(ID) # かかり先IDを取得 if token.chunk != None: ID2 = token.chunk.link toId.append(ID2) # スコアを取得 if token.chunk != None: score = token.chunk.score scores.append(score) for mrph in result.mrph_list(): # 各形態素へのアクセス so_f = re.split("[-:]", mrph.imis) # feature分割 if mrph.bunrui == '地名' or '場所' in so_f: Where.append(mrph.midasi) elif '人' in so_f or '人名' in so_f or mrph.bunrui == '人名': Who.append(mrph.midasi) for bnst in result.bnst_list(): # 各文節へのアクセス2 setu_f = re.split("[><]", bnst.fstring) del setu_f[::2] if '時間' in setu_f: When.append("".join(mrph.midasi for mrph in bnst.mrph_list())) num = len(words)-1 while num > -1: ##Why## for c in naze: if words[num] == c: #続く助詞がWhyの後に続くものの場合 Why.append(words[num]) nowId = chunkId[num] # 最初のチャンクIDを保存 while num>0: if chunkId[num-1] != nowId and toId[num-1] > nowId and words[num-1] != '(': #今のチャンクIdが最初のIdでなく, かかり先Idが最初のId出ない break num -=1 Why.insert(0,words[num]) if words[num] == 'に' and words[num-1] == 'ため': words.insert(num,'Why)') nowId = chunkId[num] # 最初のチャンクIDを保存 while num>0: if chunkId[num-1] != nowId and toId[num-1] > nowId and words[num-1] != '(': #今のチャンクIdが最初のIdでなく, かかり先Idが最初のId出ない break num -=1 #### ##How## if toId[num] == -1: while num > 0: How.insert(0, words[num]) howId.append(chunkId[num]) if (poss[num][0] =='名詞' and poss[num][1] != '非自立' and poss[num][1] != '接尾' and poss[num-1][6] != 'という'): break num -= 1 #### ##What## for c in nani: if poss[num][0] == '助詞' and words[num] == c and toId[num] in howId: #続く助詞がWhatの後に続くものの場合1 while num > 0: num -= 1 What.insert(0, words[num]) if (poss[num][0] =='名詞' and poss[num][1] != '非自立' and poss[num][1] != '接尾') or (poss[num][1] == '自立' and poss[num][6] != 'する' and poss[num][6] != 'なる'): break # num += 1 #### else: num -= 1 print('Who:%s' %(Who)) print('Where:%s' %(Where)) print('When:%s' %(When)) print('What:%s' %(What)) print('How:%s' %("".join(How)))
実行例
実行例3にて「我々」はWho要素として抽出できましたが、「キングコング」が別の要素として抽出されてしまいました。
その他の要素は概ね正しく抽出できています。
実行例1: 入力: きょうの会見に合わせたかのように宮迫さんがユーチューバーとしてまーデビューされました 出力: Who:['宮迫'] Where: When:['きょうの'] What:['宮迫', 'さん'] How:ユーチューバーとしてまーデビューされました
実行例2: 入力: 下高速道路は東名高速の下りできょう午前8時頃秦野中井インターチェンジ付近で最大30キロの渋滞が予想されるなどしています 出力: Who: Where:['下', '道路', '東名', '秦野', '中井', 'インター', '付近'] When:['きょう', '午前', '8時頃'] What:['高速道路', '渋滞'] How:予想されるなどしています
実行例3: 入力:まあ我々キングコングがスピード出世みたいなことになったんですけども 出力: Who:['我々'] Where: When: What:['キングコング'] How:出世みたいなことになったんですけども
次回の予定
次回はHow要素とWhat要素の抽出を改善したいと思います。
また、今後は大きく手法を変えることはせず、様々な例を試して問題を洗い出したいと思います。