LoginSignup
8
3

More than 5 years have passed since last update.

素人の言語処理100本ノック:57

Last updated at Posted at 2017-01-03

言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。

第6章: 英語テキストの処理

英語のテキスト(nlp.txt)に対して,以下の処理を実行せよ.

56. 係り受け解析

Stanford Core NLPの係り受け解析の結果(collapsed-dependencies)を有向グラフとして可視化せよ.可視化には,係り受け木をDOT言語に変換し,Graphvizを用いるとよい.また,Pythonから有向グラフを直接的に可視化するには,pydotを使うとよい.

出来上がったコード:

main.py
# coding: utf-8
import os
import subprocess
import xml.etree.ElementTree as ET
import pydot_ng as pydot

fname = 'nlp.txt'
fname_parsed = 'nlp.txt.xml'


def parse_nlp():
    '''nlp.txtをStanford Core NLPで解析しxmlファイルへ出力
    すでに結果ファイルが存在する場合は実行しない
    '''
    if not os.path.exists(fname_parsed):

        # StanfordCoreNLP実行、標準エラーはparse.outへ出力
        subprocess.run(
            'java -cp "/usr/local/lib/stanford-corenlp-full-2016-10-31/*"'
            ' -Xmx2g'
            ' edu.stanford.nlp.pipeline.StanfordCoreNLP'
            ' -annotators tokenize,ssplit,pos,lemma,ner,parse,dcoref'
            ' -file ' + fname + ' 2>parse.out',
            shell=True,     # shellで実行
            check=True      # エラーチェックあり
        )


def graph_from_edges_ex(edge_list, directed=False):
    '''pydot_ng.graph_from_edges()のノード識別子への対応版

    graph_from_edges()のedge_listで指定するタプルは
    識別子とグラフ表示時のラベルが同一のため、
    ラベルが同じだが実体が異なるノードを表現することができない。
    例えば文の係り受けをグラフにする際、文の中に同じ単語が
    複数出てくると、それらのノードが同一視されて接続されてしまう。

    この関数ではedge_listとして次の書式のタプルを受け取り、
    ラベルが同一でも識別子が異なるノードは別ものとして扱う。

    edge_list = [((識別子1,ラベル1),(識別子2,ラベル2)), ...]

    識別子はノードを識別するためのもので表示されない。
    ラベルは表示用で、同じでも識別子が異なれば別のノードになる。

    なお、オリジナルの関数にあるnode_prefixは未実装。

    戻り値:
    pydot.Dotオブジェクト
    '''

    if directed:
        graph = pydot.Dot(graph_type='digraph')

    else:
        graph = pydot.Dot(graph_type='graph')

    for edge in edge_list:

        id1 = str(edge[0][0])
        label1 = str(edge[0][1])
        id2 = str(edge[1][0])
        label2 = str(edge[1][1])

        # ノード追加
        graph.add_node(pydot.Node(id1, label=label1))
        graph.add_node(pydot.Node(id2, label=label2))

        # エッジ追加
        graph.add_edge(pydot.Edge(id1, id2))

    return graph


# nlp.txtを解析
parse_nlp()

# 解析結果のxmlをパース
root = ET.parse(fname_parsed)

# sentence列挙、1文ずつ処理
for sentence in root.iterfind('./document/sentences/sentence'):
    sent_id = int(sentence.get('id'))       # sentenceのid

    edges = []

    # dependencies列挙
    for dep in sentence.iterfind(
        './dependencies[@type="collapsed-dependencies"]/dep'
    ):

        # 句読点はスキップ
        if dep.get('type') != 'punct':

            # governor、dependent取得、edgesに追加
            govr = dep.find('./governor')
            dept = dep.find('./dependent')
            edges.append(
                ((govr.get('idx'), govr.text), (dept.get('idx'), dept.text))
            )

    # 描画
    if len(edges) > 0:
        graph = graph_from_edges_ex(edges, directed=True)
        graph.write_png('{}.png'.format(sent_id))

実行結果:

文の先頭からいくつかを貼り付けます。

1.png
Kobito.jeqBwe.png

2.png
Kobito.Qvt44t.png

3.png
Kobito.VO2v84.png

4.png
Kobito.MH5yWi.png

5.png
Kobito.d8dyTi.png

全体の結果はGitHubにアップしています。

係り受け解析

今回の問題は問題44の英語版です。graph_from_edges_ex()は、問題44からそのまま持ってきて使っています。

Stanford Core NLPの係り受け解析は、DependencyParseAnnotatorによるとデフォルトで3種類あるようです。問題で指定されている collapsed-dependencies はその1種類で、解析結果がxmlファイルの<dependencies type="collapsed-dependencies">タグで出力されています。

端末:nlp.txt.xmlの途中部分
        <dependencies type="collapsed-dependencies">
          <dep type="root">
            <governor idx="0">ROOT</governor>
            <dependent idx="18">field</dependent>
          </dep>
          <dep type="amod">
            <governor idx="3">processing</governor>
            <dependent idx="1">Natural</dependent>
          </dep>
          <dep type="compound">
            <governor idx="3">processing</governor>
            <dependent idx="2">language</dependent>
          </dep>
          <dep type="nmod:from">
            <governor idx="18">field</governor>
            <dependent idx="3">processing</dependent>
          </dep>
          <dep type="case">
            <governor idx="3">processing</governor>
            <dependent idx="4">From</dependent>
          </dep>
          <dep type="dep">
            <governor idx="3">processing</governor>
            <dependent idx="5">Wikipedia</dependent>
          </dep>
          <dep type="punct">
            <governor idx="18">field</governor>
            <dependent idx="6">,</dependent>
          </dep>
          <dep type="det">
            <governor idx="12">processing</governor>
            <dependent idx="7">the</dependent>
          </dep>
          <dep type="amod">
            <governor idx="12">processing</governor>
            <dependent idx="8">free</dependent>
          </dep>
          <dep type="compound">
            <governor idx="12">processing</governor>
            <dependent idx="9">encyclopedia</dependent>
          </dep>
          <dep type="amod">
            <governor idx="12">processing</governor>
            <dependent idx="10">Natural</dependent>
          </dep>
          <dep type="compound">
            <governor idx="12">processing</governor>
            <dependent idx="11">language</dependent>
          </dep>
          <dep type="nsubj">
            <governor idx="18">field</governor>
            <dependent idx="12">processing</dependent>
          </dep>
(中略)
        </dependencies>

<governor>が係り元、<dependent>が係り先の情報なので、これらを取り出せばあとは問題44と同じです。

ROOTノード

グラフを書いて見ると分かるのですが、一番最初に必ず「ROOT」というノードがあり、<dep>タグのtype属性が「root」になっていて、これが係り受け木の根っこを示すようです。Stanford typed dependencies manualの「2 Definitions of the Stanford typed dependencies」の「root:」の説明にもそれらしいことが書いてありました。

取り除いた方が良いのかも知れませんが、今回はそのまま残しています。

句読点などの記号の除去

問題42問題43で日本語の係り受け解析をやっていた時は「句読点などの記号は出力しないようにせよ」という指示がありました。今回は特に指示がありませんが、邪魔なので同様に出力を抑制しています。

<dep>タグのtype属性が「punct」のものが句読点を示しているようです。Stanford typed dependencies manualの「2 Definitions of the Stanford typed dependencies」の「punct:」の説明にもそれらしいことが書いてあるので、この「punct」を除く形で実装してみました。

 
58本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。


実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第6章で用いているデータのライセンスはクリエイティブ・コモンズ 表示-継承 3.0 非移植日本語訳)です。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3