SQuADをGoogle Translate APIで翻訳

こんにちは
AIチームの戸田です。

先日、質問応答タスクの一つ、SQuAD 2.0(The Stanford Question Answering Dataset)をGoogle Translate APIを使って翻訳しました。
正確な回答位置の翻訳が困難だったため、文書単位での回答位置推定問題になってしまいますが、なにかに応用していただけたらいいと思い、翻訳したデータをKaggle Datasetに公開しました。(元のSQuAD 2.0はCC BY-SA 4.0 licenseだったので継承しています)

今回はそのデータを作ったときの手順を紹介したいと思います。

SQuADとは

SQuADは、Wikipedia の記事の内容に対する質問とその回答を、クラウドソーシングで作られた読解タスクのデータセットです。回答となる文書が記事のどこにあるのかを人間が質問を作っているので、読解を特に重要視したタスクと言われています。

問題の1つを見てみましょう。下記はID 56be85543aeaaa14008c9063の問題です。

When did Beyonce start becoming popular?
(ビヨンセが人気になったのはいつから?)

この問題に回答するため、以下のようなcontextが与えられます。

Beyoncé Giselle Knowles-Carter (/biːˈjɒnseɪ/ bee-YON-say) (born September 4, 1981) is an American singer, songwriter, record producer and actress. Born and raised in Houston, Texas, she performed in various singing and dancing competitions as a child, and rose to fame in the late 1990s as lead singer of R&B girl-group Destiny\'s Child. Managed by her father, Mathew Knowles, the group became one of the world\'s best-selling girl groups of all time. Their hiatus saw the release of Beyoncé\'s debut album, Dangerously in Love (2003), which established her as a solo artist worldwide, earned five Grammy Awards and featured the Billboard Hot 100 number-one singles "Crazy in Love" and "Baby Boy".

回答はtextとanswer_startの2つが与えられます。

  • answer:
    • text: in the late 1990s(1990年後半)
    • answer_start: 269

これはcontextの269文字目から始まる「in the late 1990s」が正解、という意味になります。

SQuAD2.0では、contextの中に正解がない場合もあり、答えられない問題を判断しなければなりません。その際はis_impossibleという要素がTrueになっています。

Google Translate APIで翻訳

上記の例の通り、SQuADは英語のデータセットです。日本語の質問応答のデータとしては先日行われた言語処理学会で発表されたJAQKETなどがありますが、
候補の中から回答を選択するタイプで、回答箇所を抜き出すSQuADとは異なります。SQuADのような回答箇所を抜き出すタイプのデータセットが日本語であると、色々と応用できるかなと思い、Google translate APIで翻訳してみることにしました。

まずはSQuADの公式サイトからダウンロードしたデータを読み込みます。
ここではtrainデータのみ扱っていますが、形式は同様なので、validationデータも同様の方法で処理することができます。

import json

with open("train-v2.0.json", "r") as f:
    train = json.load(f)

次にGoogle Translate APIの設定をします。
APIの使い方は公式ドキュメントを参考にしました。(APIを使って翻訳するのには課金が発生するので気をつけてください。)

from google.cloud import translate_v2

os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = "<発行したAPIキー>"
translate_client = translate_v2.Client()
 
# 文章を翻訳して返す関数
def trans_ja(text):
    translation = translate_client.translate(text, target_language='ja')
    return translation["translatedText"]

いよいよ各問題を翻訳していきます。少々長めのコードになりますがご容赦ください。

import pandas as pd
from pyknp import Jumanpp
jm = Jumanpp()

# Jumanで分かち書きをする関数
def wakati_jm(text):
    result = jm.analysis(text.replace(" ", "").replace("#", ""))
    tokenized_text =[mrph.midasi for mrph in result.mrph_list()]
    return " ".join(tokenized_text)

lines = []  # 結果を格納するリスト
for data in train["data"]:
    paragraphs = data["paragraphs"]
    for paragraph in paragraphs:
        
        # contextデータの翻訳
        context = paragraph["context"]
        sents = [sent+"." for sent in context.split(". ")]
        sents = [s.replace("..", ".") for s in sents]
        sents_ja = [trans_ja(se) for se in sents]
        sents_ja_sep = [wakati_jm(s) for s in sents_ja]  # JUMANで分かち書きする
        context_ja = " ".join(sents_ja_sep)
        
        sep_ja_count = [len(s.split()) for s in sents_ja_sep]
        ja_head_i = [sum(sep_ja_count[:i]) for i in range(len(sep_ja_count))]
        ja_tail_i = [i-1 for i in ja_head_i[1:]] + [sum(sep_ja_count)]
        ja_idx = [(i, j) for i, j in zip(ja_head_i, ja_tail_i)]
        
        # 問題を翻訳する 
        for qas in paragraph["qas"]:
            if qas["is_impossible"]:  # 今回は解けない問題は無視
                continue
            question = qas["question"]
            question_ja = trans_ja(question)
            question_ja_sep = wakati_jm(question_ja)
            
            # 回答がcontext内のどこにあるのかを探索する
            answer_start = qas["answers"][0]["answer_start"]
            ans_head = 0
            for s, ja_i in zip(sents, ja_idx):
                ans_head += len(s)
                if answer_start < ans_head:
                    start, end = ja_i
                    break

            # 回答開始位置、回答終了位置、質問文、コンテキストのセットにまとめる
            line = {"start": start, "end": end, "question": question_ja_sep, "context": context_ja, "id": qas["id"]}
            lines.append(line)
           
# jsonl形式で保存
pd.DataFrame(lines).to_json('ja_squad_train.jsonl', orient='records', force_ascii=False, lines=True)

ja_squad_train.jsonlというjsonlファイルに翻訳された問題が保存されます。上記で示した例の翻訳結果は以下のようになります。

  • context: BeyoncéGiselleKnowles – Carter ( \/b i ː ˈ j ɒ nse ɪ \/ bee – YON – say ) ( 1981 年 9 月 4 日 生まれ ) は 、 アメリカ の シンガー 、 ソング ライター 、 レコード プロデューサー 、 女優 です 。 テキサス 州 ヒューストン で 生まれ育った 彼女 は 、 子供 の 頃 に さまざまな 歌 と 踊り の コンテスト に 出演 し 、 1990 年 代 後半 に R & B ガールグループ Destiny & 39 ; sChild の リード シンガー と して 名声 を 博し ました 。 父親 の マシューノウルズ が 管理 する この グループ は 、 世界 で 最も 売れて いる 少女 グループ の 1 つ に なり ました 。 彼 ら の 休み は ビヨンセ の デビュー アルバム 、 DangerouslyinLove ( 2003 ) の リリース を 見 ました 。 彼女 は 世界 中 で ソロ アーティスト と して 確立 し 、 5 つ の グラミー 賞 を 獲得 し 、 ビル ボード ホット 100 ナンバーワン シングル 「 CrazyinLove 」 と 「 BabyBoy 」 を フィーチャー し ました。
  • question: ビヨンセ は いつ から 人気 を 博し 始め ました か ?
  • answer:
    • start: 43
    • end: 88

後にTokenizerにJUMANが使われている日本語BERTによる学習を行いたいので、各文章はJUMANで単語分割しています。
回答箇所は単語分割した際の位置を示しており、回答文は

テキサス州ヒューストンで生まれ育った彼女は、子供の頃にさまざまな歌と踊りのコンテストに出演し、1990年代後半にR&BガールグループDestiny&39;sChildのリードシンガーとして名声を博しました

となります。本当は原文通り「1990年代後半」の部分のみ指定したかったのですが、短文だと翻訳が変わってしまったりと、よい方法が思い浮かばなかったため文章単位での回答位置推定問題になってしまいました。

BERTでの学習

Kaggle Notebookを使ってこの日本語版SQuADの学習をしました。
ご興味のある方は下記をご参照ください

validationデータの結果をいくつか見てみると、以下のような結果になります。

若干助長な感じもしますが、これは翻訳の際に文章ごと取ってきてしまったことが原因だと思われます。

応用案

たとえばAI Shiftの提供しているチャットボットによるカスタマーサポートにおいて、チャットボットの回答候補として製品マニュアルなどをまるごと保存しておき、問い合わせが来たときにその解答を抽出して提示する、などが考えられます。これはチャットボット設計の手間を大幅に減らすことができると思います。ただしドメインが全く異なるはずなので、ドメインデータに合わせたfine-tuningなどは必要です。

おわりに

本記事ではSQuADをGoogle Translate APIで翻訳した際の手順について紹介させていただきました。
再掲になりますが、翻訳したデータはKaggle Datasetに公開しておりますのでご自由にお使いいただければと思います。

最後までご覧にいただき、ありがとうございました

参考