yasuokaの日記: 『言語処理100本ノック 2020』「48. 名詞から根へのパスの抽出」をUniDic2UDで解いてみる
一昨日・昨日の日記に続いて、『言語処理100本ノック 2020』の「48. 名詞から根へのパスの抽出」を、UniDic2UDで解いてみることにした。ただUniDic2UDは、spaCyとの連携機能がunidic2ud.spacyに、CaboChaのエミュレータ機能がunidic2ud.cabochaに、それぞれ組み込まれていて、それぞれ一長一短あったりする。とりあえず、unidic2ud.spacyの方を中心にして、unidic2ud.cabochaの機能を一部借りてくる形で、Google Colaboratoryで書いてみた。
!pip install unidic2ud spacy
from spacy.tokens import Token,Span,Doc
Token.set_extension("surface",getter=lambda token:token.orth_,force=True)
Token.set_extension("base",getter=lambda token:token.lemma_,force=True)
Token.set_extension("pos",getter=lambda token:token.pos_,force=True)
Token.set_extension("pos1",getter=lambda token:token.tag_,force=True)
Span.set_extension("morphs",getter=lambda span:span,force=True)
Span.set_extension("dst",default=None,force=True)
Span.set_extension("srcs",getter=lambda span:{i for i,c in enumerate(span.doc._.chunks) if c._.dst==span.doc._.chunks.index(span)},force=True)
Doc.set_extension("_chunks",default=None,force=True)
def _make_chunks(doc):
chunks=doc._._chunks
if not chunks:
from unidic2ud.cabocha import Tree
from unidic2ud.spacy import to_conllu
t=Tree(to_conllu(doc))
chunks=[]
s=0
for i in range(t.chunk_size()):
e=s+t.chunk(i).token_size
c=Span(doc,s,e)
j=t.chunk(i).link
c._.dst=i if j<0 else j
chunks.append(c)
s=e
doc._._chunks=chunks
return chunks
Doc.set_extension("chunks",getter=_make_chunks,force=True)
import unidic2ud.spacy
nlp=unidic2ud.spacy.load()
doc=nlp("吾輩はここで始めて人間というものを見た")
chunks=doc._.chunks
for c in chunks:
if [t for t in c._.morphs if t._.pos in {"NOUN","PRON","PROPN"}]!=[]:
s=str(c)
d=chunks[c._.dst]
while d!=c:
s+=" -> "+str(d)
c=d
d=chunks[c._.dst]
print(s)
私(安岡孝一)の手元では、以下の結果になった。
吾輩は -> 見た
ここで -> 見た
人間という -> ものを -> 見た
ものを -> 見た
「ここで -> 見た」が妥当かどうかは疑問が残る。こんなわけで、UniDic2UDもspaCyに繋ぐことができるが、KNPやGiNZAよりは解析精度が低いようだ。まあ、UniDic2UDは、どちらかというと古文や旧字旧仮名に強いので、現代文ならばKNPかGiNZAを使うべきかな。
『言語処理100本ノック 2020』「48. 名詞から根へのパスの抽出」をUniDic2UDで解いてみる More ログイン