こんにちは、インターン生の青木です。今回は Google が 2020 年 10 月に発表した NLP モデル”multilingual-T5"(以降 mT5)を日本語データセットで動かすことができたので、 その過程と、方法を記します。実行環境は Google Colaboratory です。
mT5 とは?
Google が開発した NLP モデル"text-to-text-transfer-transformer"(以降 T5)をベースに作られたモデルです。T5 は入力と出力をテキスト形式に統一して、モデルの構造を変えることなくあらゆるタスク(分類、質疑応答、要約、翻訳など)に対応させました。ただ、これらのタスクで精度を出すためには膨大な量の事前学習とファインチューンが必要となりますが、T5 では英語での事前学習モデルしか公開されていません。 そこで出てきたのが mT5 です。このモデルは(日本語含む)101 の言語に対応し、その 101 言語で事前学習されたモデルも公開されています。それゆえに事前学習を必要とせず、目的のタスクのファインチューンのみで対応させることができるモデルになっています。
各種モジュールのインスト ール
!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といい名前で追加します。
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
Fusicでインターンをしています。