HuggingFace Transformers 4.17 : Notebooks : ゼロからの新しい言語モデルの訓練 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/14/2022 (v4.17.0)
* 本ページは、HuggingFace Transformers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- 人工知能研究開発支援
- 人工知能研修サービス(経営者層向けオンサイト研修)
- テクニカルコンサルティングサービス
- 実証実験(プロトタイプ構築)
- アプリケーションへの実装
- 人工知能研修サービス
- PoC(概念実証)を失敗させないための支援
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
- 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
- sales-info@classcat.com ; Web: www.classcat.com ; ClassCatJP
HuggingFace Transformers : Notebooks : ゼロからの新しい言語モデルの訓練
過去数カ月に渡り、新しい言語モデルをゼロから訓練する ことをこれまでより容易にする目標で transformers と tokenizers ライブラリに幾つかの改良をしました。
この投稿では、Esperanto 上で “small” モデル (84 M パラメータ = 6 層, 768 隠れサイズ, 12 アテンションヘッド) を訓練する方法を実演します – それは DistilBERT と同じ数の層 & ヘッドです。そして品詞タギングの下流タスクでモデルを再調整します。
1. データセットを探し出す
最初に、Esperanto のテキストコーパスを見つけましょう。ここでは INRIA からの OSCAR コーパス の Esperanto 部分を使用します。OSCAR は、Web の Common Crawl ダンプの言語分類とフィルタリングにより取得された巨大な多言語コーパスです。
データセットの Esperanto 部は 299 M しかありませんので、Leipzig コーパス・コレクション の Esperanto 部分コーパスと結合します、これはニュース、文献と wikipedia のような多様なソースからのテキストから成ります。
最終的な訓練コーパスは 3 GB のサイズを持ちます、これは依然として小さいです、モデルに対して、事前訓練するためにより多くのデータを取得できればより良い結果を得られます。
# in this notebook we'll only get one of the files (the Oscar one) for the sake of simplicity and performance
!wget -c https://cdn-datasets.huggingface.co/EsperBERTo/data/oscar.eo.txt
2. トークナイザーの訓練
RoBERTa と同じ特殊トークンを持つ、(GPT-2 と同じ) バイトレベルの Byte-pair エンコーディング・トークナイザーを訓練することを選択します。サイズは 52,000 であると任意に選択しましょう。
(例えば BERT のような WordPiece トークナイザーではなく) バイトレベル BPE を訓練することを勧めます、何故ならばそれは語彙を単一バイトのアルファベットから構築し始めますので、総ての単語がトークンに分解可能です (no more <unk> トークン!)。
# We won't need TensorFlow here
!pip uninstall -y tensorflow
# Install `transformers` from master
!pip install git+https://github.com/huggingface/transformers
!pip list | grep -E 'transformers|tokenizers'
# transformers version at notebook update --- 2.11.0
# tokenizers version at notebook update --- 0.8.0rc1
%%time
from pathlib import Path
from tokenizers import ByteLevelBPETokenizer
paths = [str(x) for x in Path(".").glob("**/*.txt")]
# Initialize a tokenizer
tokenizer = ByteLevelBPETokenizer()
# Customize training
tokenizer.train(files=paths, vocab_size=52_000, min_frequency=2, special_tokens=[
"<s>",
"<pad>",
"</s>",
"<unk>",
"<mask>",
])
CPU times: user 4min, sys: 3min 7s, total: 7min 7s Wall time: 2min 25s
そしてファイルをディスクにセーブしましょう :
!mkdir EsperBERTo
tokenizer.save_model("EsperBERTo")
['EsperBERTo/vocab.json', 'EsperBERTo/merges.txt']
🔥🔥 Wow, that was fast! ⚡️🔥
そして頻度によりランク付けされた最も頻度の高いトークンのリストである vocab.json とマージのリスト merges.txt の両者を持ちます。
{ "<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 # ...
素晴らしいことはトークナイザーが Esperanto に対して最適化されていることです。英語のために訓練された一般的なトークナイザーに比べて、よりネイティブな単語が単一の分割されていないトークンで表されています。発音区別符号 (= diacritics) i.e. Esperanto で使用されるアクセント付き文字 – ĉ, ĝ, ĥ, ĵ, ŝ, と ŭ – はネイティブにエンコードされます。またシークエンスをより効率的な流儀で表します。ここではこのコーパス上、エンコードされたシークエンスの平均長は事前訓練済み GPT-2 トークナイザーを使用したときよりも ~30% 小さいです。
RoBERTa 特殊トークンの処理を含み、それを tokenizers でどのように使用するかがここにあります、もちろん 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>']
3. ゼロから言語モデルを訓練する
Update : このセクションは run_language_modeling.py スクリプトに沿っていて、新しい Trainer を直接使用しています。最も好きなアプローチを自由に選択してください。
RoBERTa-like モデルを訓練します、これは幾つかの変更を持つ BERT-like なものです (詳細は ドキュメント を確認してください)。
モデルは BERT-like ですから、Masked 言語モデリングのタスクでそれを訓練します、i.e. データセットのランダムにマスクした任意のトークンをどのように埋めるかの予測です。これはサンプルスクリプトにより処理されます。
# Check that we have a GPU
!nvidia-smi
Fri May 15 21:17:12 2020 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 440.82 Driver Version: 418.67 CUDA Version: 10.1 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 Tesla P100-PCIE... Off | 00000000:00:04.0 Off | 0 | | N/A 38C P0 26W / 250W | 0MiB / 16280MiB | 0% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | No running processes found | +-----------------------------------------------------------------------------+
# Check that PyTorch sees it
import torch
torch.cuda.is_available()
True
モデルに対して以下の config を定義する
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)
最後にモデルを初期化しましょう。
Important : ゼロから訓練していますので、既存の事前訓練済みモデルやチェックポイントからではなく、config から初期化するだけです。
from transformers import RobertaForMaskedLM
model = RobertaForMaskedLM(config=config)
model.num_parameters()
# => 84 million parameters
84095008
次に訓練データセットを構築しましょう
テキストファイルにトークナイザーを適用することによりデータセットを構築します。
ここでは、1 つのテキストファイルを持つだけですので、Dataset をカスタマイズする必要さえありません。そのまま LineByLineDataset を単に使用します。
%%time
from transformers import LineByLineTextDataset
dataset = LineByLineTextDataset(
tokenizer=tokenizer,
file_path="./oscar.eo.txt",
block_size=128,
)
CPU times: user 4min 54s, sys: 2.98 s, total: 4min 57s Wall time: 1min 37s
run_language_modeling.py スクリプト内のように、data_collator を定義する必要があります。
これは単なる小さいヘルパーで、データセットの異なるサンプルをまとめて (PyTorch が逆伝播を実行する方法を知る) オブジェクトにバッチ化するのに役立ちます。
from transformers import DataCollatorForLanguageModeling
data_collator = DataCollatorForLanguageModeling(
tokenizer=tokenizer, mlm=True, mlm_probability=0.15
)
最後に、Trainer を初期化する準備が整いました。
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,
prediction_loss_only=True,
)
trainer = Trainer(
model=model,
args=training_args,
data_collator=data_collator,
train_dataset=dataset,
)
訓練開始
%%time
trainer.train()
HBox(children=(FloatProgress(value=0.0, description='Epoch', max=1.0, style=ProgressStyle(description_width='i… HBox(children=(FloatProgress(value=0.0, description='Iteration', max=15228.0, style=ProgressStyle(description_… {"loss": 7.152712148666382, "learning_rate": 4.8358287365379566e-05, "epoch": 0.03283425269240872, "step": 500} {"loss": 6.928811420440674, "learning_rate": 4.671657473075913e-05, "epoch": 0.06566850538481744, "step": 1000} {"loss": 6.789419063568115, "learning_rate": 4.5074862096138694e-05, "epoch": 0.09850275807722617, "step": 1500} {"loss": 6.688932447433472, "learning_rate": 4.343314946151826e-05, "epoch": 0.1313370107696349, "step": 2000} {"loss": 6.595982004165649, "learning_rate": 4.179143682689782e-05, "epoch": 0.1641712634620436, "step": 2500} {"loss": 6.545944199562073, "learning_rate": 4.0149724192277385e-05, "epoch": 0.19700551615445233, "step": 3000} {"loss": 6.4864857263565066, "learning_rate": 3.850801155765695e-05, "epoch": 0.22983976884686105, "step": 3500} {"loss": 6.412427802085876, "learning_rate": 3.686629892303651e-05, "epoch": 0.2626740215392698, "step": 4000} {"loss": 6.363630670547486, "learning_rate": 3.522458628841608e-05, "epoch": 0.29550827423167847, "step": 4500} {"loss": 6.273832890510559, "learning_rate": 3.358287365379564e-05, "epoch": 0.3283425269240872, "step": 5000} {"loss": 6.197585330963134, "learning_rate": 3.1941161019175205e-05, "epoch": 0.3611767796164959, "step": 5500} {"loss": 6.097779376983643, "learning_rate": 3.029944838455477e-05, "epoch": 0.39401103230890466, "step": 6000} {"loss": 5.985456382751464, "learning_rate": 2.8657735749934332e-05, "epoch": 0.42684528500131336, "step": 6500} {"loss": 5.8448616371154785, "learning_rate": 2.70160231153139e-05, "epoch": 0.4596795376937221, "step": 7000} {"loss": 5.692522863388062, "learning_rate": 2.5374310480693457e-05, "epoch": 0.4925137903861308, "step": 7500} {"loss": 5.562082152366639, "learning_rate": 2.3732597846073024e-05, "epoch": 0.5253480430785396, "step": 8000} {"loss": 5.457240365982056, "learning_rate": 2.2090885211452588e-05, "epoch": 0.5581822957709482, "step": 8500} {"loss": 5.376953645706177, "learning_rate": 2.0449172576832152e-05, "epoch": 0.5910165484633569, "step": 9000} {"loss": 5.298609251022339, "learning_rate": 1.8807459942211716e-05, "epoch": 0.6238508011557657, "step": 9500} {"loss": 5.225468152046203, "learning_rate": 1.716574730759128e-05, "epoch": 0.6566850538481744, "step": 10000} {"loss": 5.174519973754883, "learning_rate": 1.5524034672970843e-05, "epoch": 0.6895193065405831, "step": 10500} {"loss": 5.113943946838379, "learning_rate": 1.3882322038350407e-05, "epoch": 0.7223535592329918, "step": 11000} {"loss": 5.08140989112854, "learning_rate": 1.2240609403729971e-05, "epoch": 0.7551878119254006, "step": 11500} {"loss": 5.072491912841797, "learning_rate": 1.0598896769109535e-05, "epoch": 0.7880220646178093, "step": 12000} {"loss": 5.012459496498108, "learning_rate": 8.957184134489099e-06, "epoch": 0.820856317310218, "step": 12500} {"loss": 4.999591351509094, "learning_rate": 7.315471499868663e-06, "epoch": 0.8536905700026267, "step": 13000} {"loss": 4.994838352203369, "learning_rate": 5.673758865248227e-06, "epoch": 0.8865248226950354, "step": 13500} {"loss": 4.955870885848999, "learning_rate": 4.032046230627791e-06, "epoch": 0.9193590753874442, "step": 14000} {"loss": 4.941655583381653, "learning_rate": 2.390333596007355e-06, "epoch": 0.9521933280798529, "step": 14500} {"loss": 4.931783639907837, "learning_rate": 7.486209613869189e-07, "epoch": 0.9850275807722616, "step": 15000} CPU times: user 1h 43min 36s, sys: 1h 3min 28s, total: 2h 47min 4s Wall time: 2h 46min 46s TrainOutput(global_step=15228, training_loss=5.762423221226405)
🎉 最終的なモデル (+ tokenizer + config) をディスクにセーブする
trainer.save_model("./EsperBERTo")
4. LM が実際に訓練されたことを確認する
訓練と評価損失が下がるのを見るのとは別に、言語モデルが何か興味深いことを学習しているかどうかを確認する最も簡単な方法は FillMaskPipeline を使用することです。
パイプラインはトークナイザーとモデルの単純なラッパーで、’fill-mask’ は maked トークン (ここでは <mask>) を含むシークエンスを入力させて、そして最も可能性の高い filled シークエンスのリストをそれらの確率と一緒に返します。
from transformers import pipeline
fill_mask = pipeline(
"fill-mask",
model="./EsperBERTo",
tokenizer="./EsperBERTo"
)
# The sun .
# =>
fill_mask("La suno .")
[{'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}]
OK, 単純な構文/文法は機能しています。もう少し興味深いプロンプトを試してみましょう :
fill_mask("Jen la komenco de bela .")
# This is the beginning of a beautiful .
# =>
[{'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}]
5. モデルの共有 🎉
最後に、素敵なモデルを持つとき、それをコミュニティで共有することを考えてください :
- CLI : transformers-cli upload を使用してモデルをアップロードします。
- README.md モデルカードを書いてそれを model_cards/ 下のレポジトリに追加します。モデルカードは理想的には以下を含むべきです :
- モデルの説明
- 訓練 params (データセット, 前処理, ハイパーパラメータ)
- 評価結果
- 意図された用途 & 制限
- 他のどんなものでも有用です!🤓
TADA!
➡️ Your model has a page on http://huggingface.co/models and everyone can load it using AutoModel.from_pretrained(“username/model_name”).
If you want to take a look at models in different languages, check https://huggingface.co/models
以上