NLP(自然言語処理) Stanzaによる構文解析の結果を取得する

Stanza はPythonを用いて文章の構文解析を行うことができます。Stanza は多くの言語に対応し,文について様々な情報を提供します。

stanza を使用するには,事前にインストールが必要です。

UPOS

手に入る基本的な情報の一つに品詞(UPOS)があります。

UPOSは省略記号で表されます。それらの意味は以下のようになります。

1.ADJadjective形容詞
2.ADPadposition接置詞(前置詞)
3.ADVadverb副詞
4.AUXauxiliary助動詞
5.CCONJcoordinating conjunction等位接続詞
6.DETdeterminer限定詞
7.INTJinterjection間投詞
8.NOUNnoun名詞
9.NUMnumeral数詞
10.PARTparticle不変化詞
11.PRONpronoun代名詞
12.PROPNproper noun固有名詞
13.PUNCTpunctuation句読点
14.SCONJsubordinating conjunction従位接続詞
15.SYMsymbol記号
16.VERBverb動詞
17.Xotherその他

XPOS

UPOSに比べて,XPOSは詳細な品詞の情報を提供します。

それらの意味は以下のようになります。

NumberTagDescription
1.CCCoordinating conjunction等位接続詞
2.CDCardinal number基数
3.DTDeterminer限定詞:名詞や名詞句を修飾する
4.EXExistential there存在を表す語
5.FWForeign word外来語
6.INPreposition or subordinating conjunction接置詞[前置詞]または従位接続詞
7.JJAdjective形容詞
8.JJRAdjective, comparative形容詞,比較級
9.JJSAdjective, superlative形容詞,最上級
10.LSList item marker項目の先頭を示す記号
11.MDModal法助動詞
12.NNNoun, singular or mass名詞,単数または不可算
13.NNSNoun, plural名詞,複数形
14.NNPProper noun, singular固有名詞,単数形
15.NNPSProper noun, plural固有名詞,複数形
16.PDTPredeterminer前限定辞
17.POSPossessive ending所有格の終端語[‘s]
18.PRPPersonal pronoun人称代名詞
19.PRP$Possessive pronoun所有格
20.RBAdverb副詞
21.RBRAdverb, comparative副詞,比較級
22.RBSAdverb, superlative副詞,最上級
23.RPParticle不変化詞
24.SYMSymbol記号
25.TOtoto
26.UHInterjection感動詞
27.VBVerb, base form動詞,原形
28.VBDVerb, past tense動詞,過去形
29.VBGVerb, gerund or present participle動詞,動名詞または現在分詞
30.VBNVerb, past participle動詞,過去分詞
31.VBPVerb, non-3rd person singular present動詞,三人称単数以外
32.VBZVerb, 3rd person singular present動詞,三人称単数
33.WDTWh-determinerwhat や which など
34.WPWh-pronoun関係代名詞
35.WP$Possessive wh-pronoun所有代名詞:whose
36.WRBWh-adverb関係副詞

レンマ

レンマは辞書の見出し語です。例えば,take は takes, took, taken, taking に変化しますが,これらのレンマは take です。

依存関係

ある単語がどの単語に依存しているかを知ることができます。

情報の抽出

with io.open('articles_u.txt', encoding='utf-8') as f:
    text = f.read()
texts = text.replace('eos', '.\n').splitlines()

テキストを読み込みます。テキストファイルは文末を eos という記号で表しているので,ピリオドに置き換えます。

stanza.download('en')

stanza.download('en')で英語のモデルをダウンロードします。モデルは 1Gバイトを超える大きさです。一度ダウンロードされると,2回目からはダウンロードがスキップされます。モデルはテキストを解析するために必要なものです。

nlp = stanza.Pipeline(lang='en')

パイプラインの言語として英語を指定します。パイプラインは,テキストを受け取り,分析の結果を返す装置としてイメージすることができます。

for line in range(3):

ここでは,リスト texts のはじめの3つの文について構文解析を行います。

texts[0] = 'i expect all of you to be here five minutes before the test begins without fail .'
texts[1] = 'the poor old woman had her bag stolen again .'
texts[2] = 'a rush-hour traffic jam delayed my arrival by two hours .'

構文解析を行いましょう。

    doc = nlp(texts[line])

それぞれの文について構文解析を行い,オブジェクト doc に格納します。

doc =
  [
    {
      "id": 1,
      "text": "i",
      "lemma": "i",
      "upos": "PRON",
      "xpos": "PRP",
      "feats": "Case=Nom|Number=Sing|Person=1|PronType=Prs",
      "head": 2,
      "deprel": "nsubj",
      "misc": "start_char=0|end_char=1",
      "ner": "O"
    },
    {
      "id": 2,
      "text": "expect",
      "lemma": "expect",
      "upos": "VERB",
      "xpos": "VBP",
      "feats": "Mood=Ind|Tense=Pres|VerbForm=Fin",
      "head": 0,
      "deprel": "root",
      "misc": "start_char=2|end_char=8",
      "ner": "O"
    },
    {
      "id": 3,
      "text": "all",
      "lemma": "all",
      "upos": "DET",
      "xpos": "DT",
      "head": 2,
      "deprel": "obj",
      "misc": "start_char=9|end_char=12",
      "ner": "O"
    }, ......

オブジェクトの情報をリストに変換します。

    for word in doc.sentences[0].words:
        char.append(word.text)
        lemma.append(word.lemma)
        pos.append(word.pos)
        xpos.append(word.xpos)
        deprel.append(word.deprel)

for文を用いて,それぞれの単語についてオブジェクトから情報を抽出します。

    for word in doc.sentences[0].words:
        head.extend([lemma[word.head-1] if word.head > 0 else "root"])

head はどの単語に依存しているかをidで示します。オブジェクト doc を参照してください。例えば,allexpect に依存していますが,head には id として 2 が格納されています。

id では実際にどの単語に依存しているかが分からないので,リスト内表記を用いて id に対応するレンマを取得します。例えば,単語が 'all' のとき head には 'expect' が格納されます。

また,headが0の場合,head'root' を格納します。rootは他に依存する単語が存在しないことを表しています。

    chars.append(char)
    lemmas.append(lemma)
    poses.append(pos)
    xposes.append(xpos)
    heads.append(head)
    deprels.append(deprel)

それぞれの文について得られた結果をリストに追加します。

chars =
  [['i', 'expect', 'all', 'of', 'you', 'to', 'be', 'here', 'five', 'minutes', 'before', 'the', 'test', 'begins', 'without', 'fail', '.'],
   ['the', 'poor', 'old', 'woman', 'had', 'her', 'bag', 'stolen', 'again', '.'],
   ['a', 'rush', '-', 'hour', 'traffic', 'jam', 'delayed', 'my', 'arrival', 'by', 'two', 'hours', '.']]
lemmas =
  [['i', 'expect', 'all', 'of', 'you', 'to', 'be', 'here', 'five', 'minute', 'before', 'the', 'test', 'begin', 'without', 'fail', '.'],
   ['the', 'poor', 'old', 'woman', 'have', 'she', 'bag', 'steal', 'again', '.'],
   ['a', 'rush', '-', 'hour', 'traffic', 'jam', 'delay', 'my', 'arrival', 'by', 'two', 'hour', '.']]
poses =
  [['PRON', 'VERB', 'DET', 'ADP', 'PRON', 'PART', 'AUX', 'ADV', 'NUM', 'NOUN', 'SCONJ', 'DET', 'NOUN', 'VERB', 'ADP', 'NOUN', 'PUNCT'],
   ['DET', 'ADJ', 'ADJ', 'NOUN', 'VERB', 'PRON', 'NOUN', 'VERB', 'ADV', 'PUNCT'],
   ['DET', 'NOUN', 'PUNCT', 'NOUN', 'NOUN', 'NOUN', 'VERB', 'PRON', 'NOUN', 'ADP', 'NUM', 'NOUN', 'PUNCT']]
xposes =
  [['PRP', 'VBP', 'DT', 'IN', 'PRP', 'TO', 'VB', 'RB', 'CD', 'NNS', 'IN', 'DT', 'NN', 'VBZ', 'IN', 'NN', '.'],
   ['DT', 'JJ', 'JJ', 'NN', 'VBD', 'PRP$', 'NN', 'VBN', 'RB', '.'],
   ['DT', 'NN', 'HYPH', 'NN', 'NN', 'NN', 'VBD', 'PRP$', 'NN', 'IN', 'CD', 'NNS', '.']]
heads =
  [['expect', 'root', 'expect', 'you', 'all', 'here', 'here', 'expect', 'minute', 'here', 'begin', 'test', 'begin', 'here', 'fail', 'begin', 'expect'],
   ['woman', 'woman', 'woman', 'have', 'root', 'bag', 'have', 'have', 'steal', 'have'],
   ['jam', 'hour', 'hour', 'jam', 'jam', 'delay', 'root', 'arrival', 'delay', 'hour', 'hour', 'delay', 'delay']]
deprels =
  [['nsubj', 'root', 'obj', 'case', 'nmod', 'mark', 'cop', 'xcomp', 'nummod', 'obl:tmod', 'mark', 'det', 'nsubj', 'advcl', 'case', 'obl', 'punct'],
   ['det', 'amod', 'amod', 'nsubj', 'root', 'nmod:poss', 'obj', 'xcomp', 'advmod', 'punct'],
   ['det', 'compound', 'punct', 'compound', 'compound', 'nsubj', 'root', 'nmod:poss', 'obj', 'case', 'nummod', 'obl', 'punct']]

deprel については,マニュアルを参照して下さい。

全体のコードを示します。

import numpy as np
import sys
import io
import os
import stanza
#read the text
with io.open('articles_u.txt', encoding='utf-8') as f:
    text = f.read()
texts = text.replace('eos', '.\n').splitlines()
#load stanza
stanza.download('en')
nlp = stanza.Pipeline(lang='en')
chars = []
lemmas = []
poses = []
xposes = []
heads = []
deprels = []
for line in range(3):
    char = []
    lemma = []
    pos = []
    xpos = []
    head = []
    deprel = []
    print('analyzing: '+str(line+1)+' / '+str(len(texts)), end='\r')
    doc = nlp(texts[line])
    for word in doc.sentences[0].words:
        char.append(word.text)
        lemma.append(word.lemma)
        pos.append(word.pos)
        xpos.append(word.xpos)
        deprel.append(word.deprel)
    for word in doc.sentences[0].words:
        head.extend([lemma[word.head-1] if word.head > 0 else "root"])
    chars.append(char)
    lemmas.append(lemma)
    poses.append(pos)
    xposes.append(xpos)
    heads.append(head)
    deprels.append(deprel)