Huggingface Transformers 入門 (5) - 言語モデルをTrainerで学習
以下の記事を参考に書いてます。
・How to train a new language model from scratch using Transformers and Tokenizers
前回
1. はじめに
この数ヶ月間、モデルをゼロから学習しやすくするため、「Transformers」と「Tokenizers」に改良を加えました。
この記事では、「エスペラント語」で小さなモデル(84Mパラメータ= 6層、768の隠れ層、12のアテンションヘッド - DistilBERTと同じ数のレイヤーとヘッド)を事前学習した後、品詞タグ付けのファインチューニングを行います。
私たちはこのモデルの名前を「EsperBERTo」と呼ぶことにしました。
2. データセットの準備
はじめに、「エスペラント語」のコーパスを準備します。
今回は、「OSCARコーパス」のエスペラント部分を使います。
これは299Mしかないので、ニュース、文学、wikipediaなどの多様なソースからのテキストからなる「Leipzig Corpora Collection」のエスペラント部分と連結することにします。
最終的な学習コーパスのサイズは3GBです。これはまだ小さいです。事前学習のデータは、多ければ多いほど、より良い結果が得られます。
# このノートブックではシンプルさとパフォーマンスのため1ファイル(OSCAR)のみ取得
!wget -c https://cdn-datasets.huggingface.co/EsperBERTo/data/oscar.eo.txt
3. トークナイザーの学習
今回は、「RoBERTa」と同じスペシャルトークンを持つ「ByteLevelBPETokenizer」(GPT-2と同じ)を学習します。そのサイズを任意に52,000にしてみます。
私たちは、byte-level BPE(BERTのようなWordPieceトークナイザーではなく)で学習することをお勧めします。なぜなら、BPEはシングルバイトのアルファベットから語彙を構築し始めるので、全ての単語がトークンに分解可能になります。
# TensorFlowのアンインストール
!pip uninstall -y tensorflow
# Transformersのインストール
!pip install git+https://github.com/huggingface/transformers
!pip list | grep -E 'transformers|tokenizers'
%%time
from pathlib import Path
from tokenizers import ByteLevelBPETokenizer
# パス ['oscar.eo.txt']
paths = [str(x) for x in Path(".").glob("**/*.txt")]
# トークナイザーの初期化
tokenizer = ByteLevelBPETokenizer()
# 学習
tokenizer.train(files=paths, vocab_size=52_000, min_frequency=2, special_tokens=[
"<s>",
"<pad>",
"</s>",
"<unk>",
"<mask>",
])
モデルを保存します。
# モデルの保存
!mkdir EsperBERTo
tokenizer.save_model("EsperBERTo")
['EsperBERTo/vocab.json', 'EsperBERTo/merges.txt']
以下の2つのファイルが生成されます。
・vocab.json : 頻度の高いトークンのリスト
{
"<s>": 0,
"<pad>": 1,
"</s>": 2,
"<unk>": 3,
"<mask>": 4,
"!": 5,
"\"": 6,
"#": 7,
"$": 8,
"%": 9,
"&": 10,
"'": 11,
"(": 12,
")": 13,
:
・merges.txt : マージのリスト
l a
Ġ k
o n
Ġ la
t a
Ġ e
Ġ d
Ġ p
:
このトークナイザーは、「エスペラント語」用に最適化されています。英語用のトークナイザーと比較して、より多くの単語を分割されていないトークンで表現できます。エスペラント語で使われるアクセント記号である、ĉ、ĝ、ĝ、ĥ、ĵ、ŝ、ŭなどの発音記号もエンコードされています。また、より効率的な方法でシーケンスを表現しています。今回のコーパスでは、事前学習したGPT-2のトークナイザーと比べて、エンコードされた文字列の平均長さが30%ほど短くなっています。
ここでは、「RoBERTa」のスペシャルトークンの取り扱いを含め、トークナイザーでの使用方法を紹介します。もちろん、Transformersから直接使用することもできます。
トークナイザーを準備します。
from tokenizers.implementations import ByteLevelBPETokenizer
from tokenizers.processors import BertProcessing
# トークナイザーの準備
tokenizer = ByteLevelBPETokenizer(
"./EsperBERTo/vocab.json",
"./EsperBERTo/merges.txt",
)
tokenizer._tokenizer.post_processor = BertProcessing(
("</s>", tokenizer.token_to_id("</s>")),
("<s>", tokenizer.token_to_id("<s>")),
)
tokenizer.enable_truncation(max_length=512)
エンコードおよびトークン化を行います。
# エンコード
tokenizer.encode("Mi estas Julien.")
Encoding(num_tokens=7, attributes=[ids, type_ids, tokens, offsets, attention_mask, special_tokens_mask, overflowing])
# トークン化
tokenizer.encode("Mi estas Julien.").tokens
['<s>', 'Mi', 'Ġestas', 'ĠJuli', 'en', '.', '</s>']
4. 言語モデルをゼロから学習
このモデルはBERTに似ているので、Masked language modelingのタスク、つまり、データセットの中でランダムにマスクした任意のトークンをどのように埋めるかを予測するタスクで学習します。
◎ GPUの確認
はじめにGPUをチェックします。
# GPUのチェック
!nvidia-smi
2020年9月現在、P100が一番速いGPUになります。
・P100 : 計算時間 250.1sec
・T4 : 計算時間 411.4sec
・K80 : 計算時間 723.3sec
PyTorchがGPUを見ていることを確認します。
# PyTorchがGPUを見ていることを確認
import torch
torch.cuda.is_available()
◎ モデルの準備
モデルの設定を定義します。
from transformers import RobertaConfig
# モデルの設定
config = RobertaConfig(
vocab_size=52_000,
max_position_embeddings=514,
num_attention_heads=12,
num_hidden_layers=6,
type_vocab_size=1,
)
Transformersでトークナイザーを再作成します。
from transformers import RobertaTokenizerFast
# トークナイザーの作成
tokenizer = RobertaTokenizerFast.from_pretrained("./EsperBERTo", max_len=512)
最後にモデルを初期化します。
【重要】ゼロからの学習なので、事前学習されたモデルやチェックポイントからではなく、設定からのみで初期化を行います。
from transformers import RobertaForMaskedLM
# モデルの作成
model = RobertaForMaskedLM(config=config)
モデルが84億パラメータであることを確認します。
model.num_parameters()
84095008
◎ 学習データセットの準備
トークナイザーをテキストファイルに適用して、データセットを作成します。
今回は、1つのテキストファイルしかないので、Datasetをカスタマイズする必要はありません。LineByLineDatasetをそのまま使用します。
%%time
from transformers import LineByLineTextDataset
# データセットの作成
dataset = LineByLineTextDataset(
tokenizer=tokenizer,
file_path="./oscar.eo.txt",
block_size=128,
)
run_language_modeling.pyと同様に、data_collatorを定義する必要があります。
これは、データセットの異なるサンプルをバッチ処理して、PyTorchがbackpropを実行する方法を知っているオブジェクトにするための、小さなヘルパーです。
from transformers import DataCollatorForLanguageModeling
# data_collatorの作成
data_collator = DataCollatorForLanguageModeling(
tokenizer=tokenizer, mlm=True, mlm_probability=0.15
)
◎ トレーナーの準備
トレーナーを作成します。
from transformers import Trainer, TrainingArguments
# トレーナーの引数の作成
training_args = TrainingArguments(
output_dir="./EsperBERTo",
overwrite_output_dir=True,
num_train_epochs=1,
per_gpu_train_batch_size=64,
save_steps=10_000,
save_total_limit=2,
)
# トレーナーの作成
trainer = Trainer(
model=model,
args=training_args,
data_collator=data_collator,
train_dataset=dataset,
prediction_loss_only=True,
)
◎ 学習の開始
学習を開始します。
%%time
trainer.train()
最終的なモデル(トークナイザーと設定を含む)を保存します。
trainer.save_model("./EsperBERTo")
5. 言語モデルが学習されていることを確認
学習や評価の損失が減少しているかどうかはさておき、言語モデルが学習されていることを確認する最も簡単な方法は、FillMaskPipelineを使うことです。
パイプラインはトークナイザーやモデルの単純なラッパーで、'fill-mask' はマスクされたトークン(ここでは <mask>)を含むテキストを入力して、最も可能性の高いトークンのリストとその確率を返します。
from transformers import pipeline
# fill-maskパイプラインの作成
fill_mask = pipeline(
"fill-mask",
model="./EsperBERTo",
tokenizer="./EsperBERTo"
)
入力テキストを渡します。
# The sun <mask>.
fill_mask("La suno <mask>.")
[{'score': 0.02119220793247223,
'sequence': '<s> La suno estas.</s>',
'token': 316},
{'score': 0.012403824366629124,
'sequence': '<s> La suno situas.</s>',
'token': 2340},
{'score': 0.011061107739806175,
'sequence': '<s> La suno estis.</s>',
'token': 394},
{'score': 0.008284995332360268,
'sequence': '<s> La suno de.</s>',
'token': 274},
{'score': 0.006471084896475077,
'sequence': '<s> La suno akvo.</s>',
'token': 1833}]
シンプルな構文/文法はうまくいきました。もう少し面白いプロンプトを試してみます。
# This is the beginning of a beautiful <mask>.
fill_mask("Jen la komenco de bela <mask>.")
[{'score': 0.01814725436270237,
'sequence': '<s> Jen la komenco de bela urbo.</s>',
'token': 871},
{'score': 0.015888698399066925,
'sequence': '<s> Jen la komenco de bela vivo.</s>',
'token': 1160},
{'score': 0.015662025660276413,
'sequence': '<s> Jen la komenco de bela tempo.</s>',
'token': 1021},
{'score': 0.015555007383227348,
'sequence': '<s> Jen la komenco de bela mondo.</s>',
'token': 945},
{'score': 0.01412549614906311,
'sequence': '<s> Jen la komenco de bela tago.</s>',
'token': 1633}]
6. モデルの共有
最後に、素敵なモデルができたら、それをコミュニティで共有することを考えてみてください。
(1) CLI( transformers-cli upload)を使ってモデルをアップロード
(2) README.mdモデルカードを書き、model_cards/の下のリポジトリに追加。
モデルカードには、以下が含まれるのが理想的です。
・モデルの説明
・学習パラメータ(データセット、前処理、ハイパーパラメータ)
・評価結果
・意図された用途と制限
モデルは http://huggingface.co/models にページを持っており、誰もが AutoModel.from_pretrained("username/model_name") を使用してロードすることができます。
次回
この記事が気に入ったらサポートをしてみませんか?