Fusic Tech Blog

Fusion of Society, IT and Culture

自然言語処理モデルmultilingual-T5を日本語データセットで動かしてみた
2020/11/20

自然言語処理モデルmultilingual-T5を日本語データセットで動かしてみた

こんにちは、インターン生の青木です。今回はGoogleが2020年10月に発表したNLPモデル”multilingual-T5"(以降mT5)を日本語データセットで動かすことができたので、その過程と、方法を記します。実行環境は Google Colaboratoryです。

mT5とは?

Googleが開発したNLPモデル"text-to-text-transfer-transformer"(以降T5)をベースに作られたモデルです。T5は入力と出力をテキスト形式に統一して、モデルの構造を変えることなくあらゆるタスク(分類、質疑応答、要約、翻訳など)に対応させました。ただ、これらのタスクで精度を出すためには膨大な量の事前学習とファインチューンが必要となりますが、T5では英語での事前学習モデルしか公開されていません。 そこで出てきたのがmT5です。このモデルは(日本語含む)101の言語に対応し、その101言語で事前学習されたモデルも公開されています。それゆえに事前学習を必要とせず、目的のタスクのファインチューンのみで対応させることができるモデルになっています。

T5

各種モジュールのインストール

!pip install t5[gcp]==0.7.1
!pip install mesh-tensorflow==0.1.17
!pip install tensorflow-datasets==4.1.0
!pip install tensorflow-gpu==2.3.0
!pip install tensorflow-text==2.3.0
!pip install allennlp==0.9.0
!apt-get install mecab mecab-ipadic-utf8
!pip install mecab-python3 sumeval
!apt-get install nkf
!pip install python-Levenshtein
!pip install janome

モジュールのインストールが終わったら次は、mT5本体をgit cloneしてきます。

!git clone https://github.com/google-research/multilingual-t5
!cp -r /content/multilingual-t5/multilingual_t5 .
!rm -r multilingual-t5
!cd text-to-text-transfer-transformer && git checkout bf46737

mT5本体のクローンが終わったら次はデータセットの準備です、今回はこちらの記事を参考に優しい日本語データセットを作製しファインチューニングしていきます。

データセットの準備

#データセットのダウンロード
!curl -L -o T15.xlsx "https://filedn.com/lit4DCIlHwxfS1gj9zcYuDJ/SNOW/T15-2020.1.7.xlsx"
!curl -L -o T23.xlsx "https://filedn.com/lit4DCIlHwxfS1gj9zcYuDJ/SNOW/T23-2020.1.7.xlsx"

ダウンロードしたデータセットの加工&保存

import pandas as pd
snow_t15 = pd.read_excel('T15.xlsx')
snow_t15 = snow_t15.rename(columns={'#日本語(原文)': 'input', '#やさしい日本語':'target'})[['ID', 'input','target']]

import Levenshtein
def levenshtein_distance(row):
  return Levenshtein.distance(row["target"], row["input"])

snow_t15['levenshtein_distance'] = snow_t15.apply(levenshtein_distance, axis=1)
snow_t15 = snow_t15.query('levenshtein_distance < 10')[['input', 'target']]
snow_t15.to_csv("temp.tsv", sep="\t", header=False, index=False)
!cat temp.tsv | nkf -m0Z1 | tr "[:upper:]" "[:lower:]" > snow_t15.tsv
snow_t23 = pd.read_excel('T23.xlsx')
snow_t23 = snow_t23.rename(columns={'#日本語(原文)': 'input', '#やさしい日本語':'target'})[['ID', 'input','target']]
snow_t23['levenshtein_distance'] = snow_t23.apply(levenshtein_distance, axis=1)
snow_t23 = snow_t23.query('levenshtein_distance < 10')[['input', 'target']]
snow_t23.to_csv("temp.tsv", sep="\t", header=False, index=False)
!cat temp.tsv | nkf -m0Z1 | tr "[:upper:]" "[:lower:]" > snow_t23.tsv

加工したデータセットは
train.tsv
val.tsv
test.tsv
として保存します。

!cat snow_t15.tsv snow_t23.tsv > snow.tsv
with open("snow.tsv", "r") as f:
  lines = f.readlines()
  lines = [line.strip() for line in lines if len(line) > 5]
from sklearn.model_selection import train_test_split
train, dev_test = train_test_split(lines, train_size=0.8, random_state=4)
dev, test = train_test_split(dev_test, train_size=0.5, random_state=7)

with open("train.tsv", "w") as f:
    f.write("\n".join(train)+"\n")
with open("val.tsv", "w") as f:
    f.write("\n".join(dev)+"\n")
with open("test.tsv", "w") as f:
    f.write("\n".join(test)+"\n")

これで、データセットの準備が整いました。mT5をこのデータで学習させるためには"TaskRegistry”にtaskとしてデータセットを追加してあげる必要があります。 multilingual_t5を開きtaks.pyに以下のコードを追記します。 今回は優しい日本語タスクをsnowといい名前で追加します。

Google Colaboratory

from sumeval.metrics.lang.lang_ja import LangJA
from sacrebleu import corpus_bleu, TOKENIZERS

lang_ja = LangJA()
def tokenizer_ja(text):
  words = lang_ja.tokenize_with_preprocess(text)
  return " ".join(words)
TOKENIZERS["ja"] = tokenizer_ja

def bleu(targets, predictions):
  predictions = [tf.compat.as_text(x) for x in predictions]

  if isinstance(targets[0], list):
    targets = [[tf.compat.as_text(x) for x in target] for target in targets]
  else:
    targets = [tf.compat.as_text(x) for x in targets]
    targets = [targets]

  bleu_score = corpus_bleu(predictions, targets,smooth_method="exp", smooth_value=0.0,
                 force=False,lowercase=False,tokenize="ja", use_effective_order=False)
  return {"bleu": bleu_score.score}

snow_tsv_path = {
    "train": "./train.tsv",
    "validation": "./val.tsv",
    "test": "./test.tsv",
}

t5.data.TaskRegistry.add(
    'snow',
    t5.data.TextLineTask,
    split_to_filepattern=snow_tsv_path,
    text_preprocessor=[
      functools.partial(
          preprocessors.parse_tsv,
          field_names=["inputs", "targets"]),
    ],
    output_features=DEFAULT_OUTPUT_FEATURES,
    metric_fns=[bleu])

これでTaskRegistryに優しい日本語への翻訳タスクがsnowとして追加されました。 次は、同じくmultilingual_t5にあるpreprocessors.pyに

from t5.data.preprocessors import parse_tsv

を追記します、これでTaskRegistryのsnowを実行できるようになりました。

学習済みモデルのダウンロード

学習済みモデルとファインチューンモデルを入れるディレクトリを作り、 GCSからGoogleが公開している学習済みモデルを持ってきます。 公開されているモデルはパラメーター数ごとにsmallからXXLまでありますが、おそらくメモリに乗り切らないのでsmallを使います。

!mkdir pretrained_model
!mkdir finetuned_model
!gsutil cp -r gs://t5-data/pretrained_models/mt5/small/model* ./pretrained_model/
!gsutil cp -r gs://t5-data/pretrained_models/mt5/small/checkpoint ./pretrained_model/
!gsutil cp -r gs://t5-data/pretrained_models/mt5/small/operative_config.gin ./pretrained_model/
!gsutil cp -r gs://t5-data/pretrained_models/mt5/small/pretrain_operative_config.gin ./pretrained_model/

学習

準備が整ったのでいよいよ学習です。 以下のスクリプトをGoogle Colaboratory にそのまま貼り付けて実行すれば学習が始まります。ファインチューニングステップは3000stepにしてあり、大体1時間くらいで学習は終了します

!export PYTHONPATH=${PYTHONPATH}:. && \
  \
  PRE_TRAINED_MODEL_DIR='./pretrained_model/' && \
  OPERATIVE_CONFIG=$PRE_TRAINED_MODEL_DIR'/operative_config.gin' && \
  FINE_TUNED_MODEL_DIR='./finetuned_model' && \
  FINE_TUNING_BATCH_SIZE=1024 && \
  PRE_TRAINGING_STEPS=1000000 && \
  FINE_TUNING_STEPS=`expr $PRE_TRAINGING_STEPS + 3000` && \
  INPUT_SEQ_LEN=64 &&\
  TARGET_SEQ_LEN=64 &&\
  \
  echo "OPERATIVE_CONFIG=$OPERATIVE_CONFIG" &&\
  echo "FINE_TUNED_MODEL_DIR=$FINE_TUNED_MODEL_DIR" &&\
  echo "FINE_TUNING_BATCH_SIZE=$FINE_TUNING_BATCH_SIZE" &&\
  echo "PRE_TRAINGING_STEPS=$PRE_TRAINGING_STEPS" &&\
  echo "FINE_TUNING_STEPS=$FINE_TUNING_STEPS" && \
  echo "INPUT_SEQ_LEN=$INPUT_SEQ_LEN" && \
  echo "TARGET_SEQ_LEN=$TARGET_SEQ_LEN" && \
  \
  t5_mesh_transformer \
  --model_dir="$FINE_TUNED_MODEL_DIR" \
  --module_import="multilingual_t5.tasks" \
  --gin_file="dataset.gin" \
  --gin_file="$OPERATIVE_CONFIG" \
  --gin_param="run.layout_rules=''" \
  --gin_param="run.mesh_shape=''" \
  --gin_param="utils.get_variable_dtype.activation_dtype='float32'" \
  --gin_param="MIXTURE_NAME = 'snow'" \
  --gin_file="learning_rate_schedules/constant_0_001.gin" \
  --gin_param="run.train_steps=$FINE_TUNING_STEPS" \
  --gin_param="run.sequence_length = {'inputs': $INPUT_SEQ_LEN, 'targets': $TARGET_SEQ_LEN}" \
  --gin_param="run.save_checkpoints_steps=1000" \
  --gin_param="run.batch_size=('tokens_per_batch', $FINE_TUNING_BATCH_SIZE)"

推論

優しい日本語に変換する文章をinput.txtとして作製します。

%%bash
cat <<EOF > inputs.txt
彼らは高飛びしたらしい。
彼は分析のスペシャリストだ。
吾輩は猫である。
彼は今時のナウい若者です。
あなたをプロジェクトリーダーにアサインします。
EOF

以下のスクリプトで推論が実行出来ます。推論が実行されるとout.txtが作成され、その中に推論結果が記述されます。

!export PYTHONPATH=${PYTHONPATH}:. && \
  \
  FINE_TUNED_MODEL_DIR='./finetuned_model' && \
  OPERATIVE_CONFIG=$FINE_TUNED_MODEL_DIR'/operative_config.gin' && \
  \
  echo "OPERATIVE_CONFIG=$OPERATIVE_CONFIG" &&\
  echo "FINE_TUNED_MODEL_DIR=$FINE_TUNED_MODEL_DIR" &&\
  \
  t5_mesh_transformer \
  --model_dir="$FINE_TUNED_MODEL_DIR" \
  --module_import="multilingual_t5.tasks" \
  --gin_file="$OPERATIVE_CONFIG" \
  --gin_param="run.layout_rules=''" \
  --gin_param="run.mesh_shape=''" \
  --gin_file="infer.gin" \
  --gin_file="beam_search.gin" \
  --gin_param="utils.get_variable_dtype.slice_dtype='float32'" \
  --gin_param="utils.get_variable_dtype.activation_dtype='float32'" \
  --gin_param="run.batch_size=('tokens_per_batch', 1024)" \
  --gin_param="infer_checkpoint_step = 1010000" \
  --gin_param="input_filename = './inputs.txt'" \
  --gin_param="output_filename = './out.txt'" 2>&1 | tee infer.log

実際の推論結果は3000stepで以下のようになりました。

彼らは高飛んだらしい。
彼は分析のスペシャリストだ。
吾輩は猫である。
彼は今時のナウい若い人です。
あなたをプロジェクトリーダーにアサインします。

3000stepでは、あまり推論結果がかわりませんでした。時間はかかりますが10000stepほど学習させると、もしかしたらいい結果が得られるかもしれません。

aoki masataka

aoki masataka

Fusicでインターンをしています。