パスワードを忘れた? アカウント作成
15082472 journal
人工知能

yasuokaの日記: spaCy-SynChaの単語間係り受けと文節間係り受け

日記 by yasuoka

spaCy-SynChaにもbunsetu_spansbunsetu_spanが準備されており、昨日の日記と同様、spaCyのDoc・Span・Tokenが、日本語の文章・文節・単語に対応している。

>>> import spacy_syncha
>>> nlp=spacy_syncha.load()
>>> doc=nlp("私の名前は中野です。")
>>> print(doc,type(doc),len(doc))
私の名前は中野です。 <class 'spacy.tokens.doc.Doc'> 7

spaCy-SynChaの係り受け解析結果は、spaCyのDoc(文章)として返される。単語(Token)の配列だと考えていい。上の「私の名前は中野です。」は7つの単語から構成されており、doc[0]doc[1]、…、doc[6]で参照できる。とりあえず、doc[2]に注目してみよう。

>>> token=doc[2]
>>> print(token.orth_,type(token),token.lemma_,token.tag_,token.pos_)
名前 <class 'spacy.tokens.token.Token'> 名前 名詞-一般 NOUN

Tokenにおける情報のうち、基本的なところとしては.orth_(表層形)、.lemma_(辞書見出し形)、.tag_(品詞)、.pos_(Part-Of-Speech)がある。doc[2]の「名前」であれば、表層形と辞書見出し形は両方とも「名前」、品詞は「名詞-一般」、Part-Of-Speechは「NOUN」であることがわかる。

>>> print(token.i,token.doc)
2 私の名前は中野です。

Tokenが、Docの何番目の要素にあたるかは、.iに入っている。また、TokenからDocへ戻りたい場合は.docを使う。

>>> span=spacy_syncha.bunsetu_span(token)
>>> print(span,type(span),len(span))
名前は <class 'spacy.tokens.span.Span'> 2

単語(Token)を含む文節(Span)が欲しい場合は、bunsetu_span(Token)を使う。上の例では「名前」を含む文節「名前は」を得ている。SpanもTokenの配列とみなせる。上の「名前は」であれば、span[0]に「名前」、span[1]に「は」のTokenが入っている。

>>> print(span.start,span.end,span.root)
2 4 名前

文節(Span)の主辞(中心となるToken)は、.rootに入っている。また、Spanを単語の配列として見た時、元のDocの何番目から何番目の要素にあたるかは、.start.endに入っている。上の例では、文節「名前は」はdoc[2:4](すなわちdoc[2]doc[3])にあたり、主辞は「名前」である。

>>> spanlist=spacy_syncha.bunsetu_spans(doc)
>>> print(spanlist,type(spanlist),len(spanlist))
[私の, 名前は, 中野です。] <class 'list'> 3

文章(Doc)に含まれる全ての文節が欲しい場合は、bunsetsu_spans(Doc)を使う。Spanのリストが返されるので、注意されたい。上の例では「私の」「名前は」「中野です。」の3つのSpanが得られている。

ここまでが理解できたなら、次に単語間係り受けに関して、順に見ていこう。

>>> token=doc[2]
>>> print(token,token.dep_,token.head)
名前 nsubj 中野

Token(単語)の.headには、別のTokenが入っており、単語間係り受けを表す。.dep_は、係り受けの種類を示すタグである。上の例では、「名前」←「中野」という係り受けにnsubj(主語)というタグがつけられており、「名前」が「中野」の主語であることを示している。

>>> t=token.head
>>> print(t,t.dep_,t.head,t==t.head)
中野 ROOT 中野 True

単語間係り受けを辿っていくと、.headに自分自身が入っているTokenに行き当たる。これが、単語間係り受けにおける「ROOT Token」であり、「私の名前は中野です。」では「中野」が「ROOT Token」にあたる。

>>> print(list(t.lefts),t,list(t.rights))
[名前] 中野 [です, 。]

単語間係り受けを逆向きに辿るイテレータとして、Tokenには.lefts(文頭方向)と.rights(文末方向)が準備されている。上の例では、「名前」←「中野」、「中野」→「です」、「中野」→「。」という単語間係り受けが、「中野」から出ていることがわかる。

>>> import deplacy
>>> deplacy.render(doc,Japanese=True)
私   PRON  ═╗<╗     nmod(体言による連体修飾語)
の   ADP   <╝ ║     case(格表示)
名前 NOUN  ═╗═╝<╗   nsubj(主語)
は   ADP   <╝   ║   case(格表示)
中野 PROPN ═╗═══╝═╗ ROOT(親)
です AUX   <╝     ║ cop(繫辞)
。   PUNCT <══════╝ punct(句読点)

なお、単語間係り受けを可視化するツールとしては、deplacyがオススメだ。文章(Doc)内の全ての単語間係り受けを、テキスト環境で一望できる。

以下に述べるspaCy-SynChaの文節間係り受けは、実は、spaCyではなくCaboChaの係り受け解析結果を反映している。ただ、文節間係り受けの矢印は、単語間係り受けとは逆方向に書くので、注意されたい。

>>> spanlist=spacy_syncha.bunsetu_spans(doc)
>>> span=spanlist[1]
>>> print(list(span.lefts),span,list(span.rights))
[私] 名前は []

文節(Span)にも、.lefts.rightsが準備されている。ただし、CaboChaの係り受け解析結果において、Spanの.rightsは通常は何も返さない。また、これらが返すのは、Token(単語)のイテレータであり、Spanでは無い。

>>> s={spacy_syncha.bunsetu_span(t) for t in span.lefts}
>>> print(s,span)
{私の} 名前は

そこで、bunsetu_span(Token)を使って、単語(Token)を文節(Span)へと拡張する。上の例では「私の」→「名前は」という文節間係り受けが、抽出されていることになる。

>>> from deplacy.deprelja import deprelja
>>> s={(spacy_syncha.bunsetu_span(t),deprelja[t.dep_]) for t in span.lefts}
>>> print(s,span)
{(私の, '体言による連体修飾語')} 名前は

なお、文節間係り受けの種類を知りたい場合にも、deplacyが便利である。端的には、Tokenの.dep_(タグ)をdepreljaで読み替えればいい。上の例では、「私の」→「名前は」という文節間係り受けが、「体言による連体修飾語」(nmod)であることがわかる。

spaCy-SynChaは、『形態素解析部の付け替えによる近代日本語(旧字旧仮名)の係り受け解析』でも書いたとおり、MeCabの上にCaboChaを乗せて、CaboChaの上にSynChaを乗せて、さらにSynChaの上にspaCyを乗せている。その際、CaboChaのバグに捕まらないよう、python3インターフェースは、私(安岡孝一)がspaCy準拠で作りなおした。インストールは手間だが、ぜひspaCy-SynChaも試してみてほしい。

この議論は、yasuoka (21275)によって ログインユーザだけとして作成されたが、今となっては 新たにコメントを付けることはできません。
typodupeerror

身近な人の偉大さは半減する -- あるアレゲ人

読み込み中...