ナード戦隊データマン

機械学習, 自然言語処理, データサイエンスについてのブログ

係り受けパスによるインデクシングを日英に一般化

前回の係り受けパスによるインデクシングのためのアルゴリズムは、ginzaで日本語に対して適用することだけを念頭においていました。今回は、spacyの英語モデルとginzaの日本語モデルの両方に対する一般化を行います。

github.com

アルゴリズム概要

  1. spacyで文の依存関係を抽出。
  2. ROOTにつながるパスをすべて取得。
  3. パス内に特定の係り受けタイプのノードのみを残す。

前回との違いは、前回はパスを求める前に係り受けタイプを決定しましたが、今回はパスを求めた上で、パスを縮小するために係り受けタイプを使います。この縮小では、[0,1,2]というパスがあって、1を削除した場合は、エッジの有無に関わらず[0,2]というパスに置き換えることを意味します。

これは、ginzaがobjに対して名詞をつなげる傾向にあるのに対し、spacyの公開英語モデルは名詞以外の語をつなげることがあるため行った一般化です。

モジュール

# coding: utf-8

import spacy
import networkx as nx

NLP = None

def load_global_nlp(lang="ja"):
    global NLP
    if lang == "ja":
        NLP = spacy.load("ja_ginza")
        NLP.tokenizer.set_enable_ex_sudachi(True)
    else:
        NLP = spacy.load("en_core_web_lg")
    return NLP

def build_path(edges_list):
    G = nx.DiGraph()
    edges = []
    identity = {}
    root = None
    objs = []
    for edges_ in edges_list:
        for edge, token in edges_.items():
            if edge[1] is None:
                root = edge[0]
                edge = (edge[0], edge[0])
            elif token.dep_ == "obj" or token.dep_ == "dobj":
                objs += [edge]
            edges += [edge]
            identity[edge[0]] = token
    objs = [o[0] for o in objs if o[1] == root]
    yield identity
    assert root is not None
    G.add_edges_from(edges)
    for k, v in identity.items():
        if (nx.has_path(G, k, root)) and (any([nx.has_path(G, k, obj) or k==obj for obj in objs]) or k == root):
            yield k, v.lemma_, nx.shortest_path(G, k, root)
    
            
def cut_path(path, identity, ntypes="amod,nmod,conj,compound,pobj,poss"):
    tps = ntypes.strip().split(",")
    out = []
    for nid in path:
        if identity[nid].dep_ in tps+["ROOT", "dobj", "obj"]:
            out += [nid]
    return tuple(out)
    
def extract_advance(text, lang="ja"):
    global NLP
    nlp = NLP
    doc = nlp(text)
    out = []
    is_exist_root = False
    for sent in doc.sents:
        for token in sent:
            if "ROOT" == token.dep_:
                out += [((token.i, None), token)]
                is_exist_root = True
            else:
                out += [((token.i, token.head.i), token)]
    if is_exist_root:
        out = dict(out)
        return build_path([out])
    else:
        return None, None, None

モジュールを使う

モジュールを使った例は以下のノートブックに保存しています。

https://github.com/sugiyamath/text_commonality_experiments/blob/master/experiments/notebook4.ipynb

ノートブックの出力は以下のデータです。

パスを部分的に視覚化すると以下のようになります。

参考

  1. https://spacy.io/usage/linguistic-features#dependency-parse
  2. [“GiNZA - Japanese NLP Library”] | [“Universal Dependenciesに基づくオープンソース日本語NLPライブラリ”]