ナード戦隊データマン

機械学習と自然言語処理についてのブログ

words2textのためのアノテーション

文の自然さを評価するための2値分類モデルがあれば、文の生成タスクにおいて、いくつかの候補文の中から不自然な文を除外したり、ランク付けすることに使えます。ここでは、そのような目的で使う「文の自然さ」のためのアノテーション方法を考えます。

Words2Text

私が行いたいタスクは、「Words2Text」という種類のタスクです。ググってもこのタスクは見つからないと思いますが、これは複数の単語を与えた時、その単語を使って文を生成します。

例えば、

入力: 家, ゴキブリ, 殺虫剤 期待される出力の例: 家でゴキブリが出たので殺虫剤で殺した

私は単純な文の生成のために、述語項構造シソーラスを用いることを考えています。事前に「述語」だけがわかっていると仮定して、述語項構造シソーラスからその述語に対するテンプレートを取得し、テンプレートに入力語の順列を当てはめる、というタスクを考えています。

文の自然さの自動アノテーション

BCCWJのようなコーパスを持っていると仮定します。このようなコーパスを、文単位で区切って取得した時、これらの文は「すべて自然な文である」と仮定します。

述語項構造シソーラスでは、

動く(x, y, z)

のような形式で、動詞と動詞の項からなるテンプレートがある状態です。私が解きたいタスクでは、述語は事前に判明しているという仮定なので、求めたいのは「そこに当てはめる項(とその順番)」ということになります。

そこで、BCCWJの自然な文のリストから不自然な文を生成するために、以下の方法を使います。

方法: 各文の名詞をすべての順列として入れ替える。入れ替えてないものをTrue, それ以外をFalseにする。

これにより、distant supervision的に自動アノテーションが可能です。

コード

# coding: utf-8
import MeCab
from tqdm import tqdm
from itertools import permutations

def split_custom(sentence, tagger):
    try:
        lines = [x.split("\t") for x in tagger.parse(sentence).split("\n")]
        lines = [(x[0], x[1].split(",")[0]) for x in lines if len(x) == 2]
        return lines
    except Exception as e:
        print(e)
        return []


def has_noun(ss):
    for s in ss:
        if s[1] == "名詞":
            return True
    return False


def noun_perm(ss):
    temp = ""
    ns = []
    for s in ss:
        if s[1] == "名詞":
            temp += "{}"
            ns.append(s[0])
        else:
            temp += s[0]
    return temp, ns


def run(out, outfile="annotated.txt"):
    with open(outfile, "w") as f:
        for ss in tqdm(out):
            temp, ns = noun_perm(ss)
            if len(ns) > 5:
                continue
            for i, x in enumerate(permutations(ns)):
                if i == 0:
                    label = "1"
                else:
                    label = "0"
                f.write("{}\t{}\n".format(temp.format(*x),label))



if __name__ == "__main__":
    tagger = MeCab.Tagger("-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd")

    with open("data.txt") as f:
        out = []
        for line in tqdm(f):
            line = line.strip()
            tmp = split_custom(line, tagger)
            if tmp:
                out.append(tmp)


    out = [ss for ss in tqdm(out) if has_noun(ss)]
    run(out)

補足

ちなみに、このような特殊なタスクではなく、より一般的に「文の自然さ」を評価したい場合は、名詞を入れ替えるだけではなく、すべての形態素を入れ替えることによって不自然な文が生成できます。

確かに、一定の確率で「入れ替えた文が自然になる」ようなケースもありますが、そのようなケースは稀です。

リンク

述語項構造シソーラス - http://pth.cl.cs.okayama-u.ac.jp/ BCCWJ - https://pj.ninjal.ac.jp/corpus_center/bccwj/