ホーム » HuggingFace Transformers » HuggingFace Transformers 4.17 : Notebooks : ゼロからの新しい言語モデルの訓練

HuggingFace Transformers 4.17 : Notebooks : ゼロからの新しい言語モデルの訓練

HuggingFace Transformers 4.17 : Notebooks : ゼロからの新しい言語モデルの訓練 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/14/2022 (v4.17.0)

* 本ページは、HuggingFace Transformers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス

クラスキャット は人工知能・テレワークに関する各種サービスを提供しています。お気軽にご相談ください :

◆ 人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。スケジュール
  • お住まいの地域に関係なく 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

 

以上



ClassCat® Chatbot

人工知能開発支援
クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com

カテゴリー