「TransformersとTokenizersを使用して、ゼロから新しい言語モデルを訓練する方法」
Training a new language model from scratch using Transformers and Tokenizers
ここ数か月間で、私たちはtransformers
とtokenizers
ライブラリにいくつかの改良を加え、新しい言語モデルをゼロからトレーニングすることをこれまで以上に簡単にすることを目指しました。
この記事では、”小さな”モデル(84 Mパラメータ = 6層、768隠れユニット、12アテンションヘッド)を「エスペラント」でトレーニングする方法をデモンストレーションします。その後、モデルを品詞タグ付けの下流タスクでファインチューニングします。
エスペラントは学習しやすいことを目標とした人工言語です。このデモンストレーションのために選んだ理由は以下のとおりです:
- 「Microsoft Azureの新しいディープラーニングと自然言語処理のチュートリアルを発表します」
- テキストの生成方法:トランスフォーマーを使用した言語生成のための異なるデコーディング方法の使用方法
- 「The Reformer – 言語モデリングの限界を押し上げる」
- 比較的リソースが少ない言語です(約200万人が話すにもかかわらず)、このデモンストレーションはもう1つの英語モデルのトレーニングよりも面白くなります 😁
- 文法が非常に規則的です(例:一般的な名詞は-oで終わり、すべての形容詞は-aで終わります)。そのため、小さなデータセットでも興味深い言語的結果が得られるはずです。
- 最後に、この言語の基盤となる目標は人々をより近づけることです(世界平和と国際理解を促進すること)。これはNLPコミュニティの目標と一致していると言えるでしょう 💚
注:この記事を理解するためにはエスペラントを理解する必要はありませんが、学びたい場合はDuolingoには280,000人のアクティブな学習者がいる素敵なコースがあります。
私たちのモデルの名前は…待ってください…EsperBERTo 😂
1. データセットを見つける
まず、エスペラントのテキストコーパスを見つけましょう。ここでは、INRIAのOSCARコーパスのエスペラント部分を使用します。OSCARは、WebのCommon Crawlダンプの言語分類とフィルタリングによって得られた巨大な多言語コーパスです。
データセットのエスペラント部分はわずか299Mですので、Leipzig Corpora Collectionのエスペラントサブコーパスと連結します。このサブコーパスには、ニュース、文学、ウィキペディアなど様々なソースのテキストが含まれています。
最終的なトレーニングコーパスのサイズは3 GBですが、モデルに先行学習するためのデータが多ければ多いほど、より良い結果が得られます。
2. トークナイザーをトレーニングする
GPT-2と同じ特殊トークンを持つバイトレベルのバイトペアエンコーディングトークナイザーをトレーニングすることにしましょう。そのサイズは任意に52,000とします。
私たちは、バイトレベルのBPE(BERTのWordPieceトークナイザーの代わりに)をトレーニングすることをお勧めします。なぜなら、バイト単位のアルファベットから語彙を構築し始めるため、すべての単語はトークンに分解できるからです(<unk>
トークンが必要ありません!)。
#! pip install tokenizers
from pathlib import Path
from tokenizers import ByteLevelBPETokenizer
paths = [str(x) for x in Path("./eo_data/").glob("**/*.txt")]
# トークナイザーを初期化
tokenizer = ByteLevelBPETokenizer()
# トレーニングをカスタマイズ
tokenizer.train(files=paths, vocab_size=52_000, min_frequency=2, special_tokens=[
"<s>",
"<pad>",
"</s>",
"<unk>",
"<mask>",
])
# ファイルをディスクに保存
tokenizer.save_model(".", "esperberto")
そして、出力のわずかに加速されたキャプチャは次のようになります:
私たちのデータセットでは、トレーニングに約5分かかりました。
🔥🔥 うわー、それは速い! ⚡️🔥
これで、最も頻度の高いトークンのリストである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
# ...
素晴らしいことは、私たちのトークナイザーはエスペラントに最適化されていることです。一般的な英語のトークナイザーと比較して、より多くのネイティブワードが1つの分割されていないトークンで表されます。エスペラントで使用されるアクセント記号(ĉ
、ĝ
、ĥ
、ĵ
、ŝ
、ŭ
)もネイティブにエンコードされます。また、シーケンスもより効率的な方法で表現されます。このコーパスでは、事前学習済みのGPT-2トークナイザーを使用する場合と比較して、エンコードされたシーケンスの平均長は約30%短くなります。
以下は、tokenizers
を使用してそれをどのように利用できるかを示したものです。もちろん、transformers
から直接使用することもできます。
from tokenizers.implementations import ByteLevelBPETokenizer
from tokenizers.processors import BertProcessing
tokenizer = ByteLevelBPETokenizer(
"./models/EsperBERTo-small/vocab.json",
"./models/EsperBERTo-small/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)
print(
tokenizer.encode("Mi estas Julien.")
)
# Encoding(num_tokens=7, ...)
# tokens: ['<s>', 'Mi', 'Ġestas', 'ĠJuli', 'en', '.', '</s>']
3. ゼロから言語モデルをトレーニングする
更新: 関連するColabノートブックは、スクリプト経由ではなく、直接新しいTrainer
を使用します。お好みのアプローチを選んでください。
次に、transformers
のrun_language_modeling.py
スクリプトを使用して、言語モデルをトレーニングします(run_lm_finetuning.py
から改名され、ゼロからのトレーニングをよりシームレスにサポートするようになりました)。ただし、既存のモデルやチェックポイントからトレーニングする場合は、--model_name_or_path
をNone
のままにしておく必要があります。
私たちは、変更がいくつか加えられたBERTのようなモデルであるRoBERTaのようなモデルをトレーニングします(詳細についてはドキュメントを参照してください)。
モデルがBERTのようなモデルであるため、データセット内のランダムにマスクされた任意のトークンを埋める方法を予測するマスク言語モデリングのタスクでトレーニングします。この例のスクリプトによって処理されます。
以下の2つのことを行う必要があります:
- データをロードする
Dataset
のシンプルなサブクラスを実装する- 使用用途によっては、提供されている例(
TextDataset
とLineByLineTextDataset
)のいずれかを使用するだけで、独自のDataset
のサブクラスを書く必要はないかもしれませんが、コーパスの特性に基づいてカスタマイズを行いたい場合もあります。
- 使用用途によっては、提供されている例(
- 異なるハイパーパラメータのセットを選択して実験する。
以下は、私たちのEsperantoDatasetのシンプルなバージョンです。
from torch.utils.data import Dataset
class EsperantoDataset(Dataset):
def __init__(self, evaluate: bool = False):
tokenizer = ByteLevelBPETokenizer(
"./models/EsperBERTo-small/vocab.json",
"./models/EsperBERTo-small/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)
# or use the RobertaTokenizer from `transformers` directly.
self.examples = []
src_files = Path("./data/").glob("*-eval.txt") if evaluate else Path("./data/").glob("*-train.txt")
for src_file in src_files:
print("🔥", src_file)
lines = src_file.read_text(encoding="utf-8").splitlines()
self.examples += [x.ids for x in tokenizer.encode_batch(lines)]
def __len__(self):
return len(self.examples)
def __getitem__(self, i):
# We’ll pad at the batch level.
return torch.tensor(self.examples[i])
データセットが非常に大きい場合、前処理のステップではなく、必要に応じて例を動的に読み込んでトークン化することができます。
以下はスクリプトに渡すハイパーパラメータと引数の一つの具体的なセットです:
--output_dir ./models/EsperBERTo-small-v1
--model_type roberta
--mlm
--config_name ./models/EsperBERTo-small
--tokenizer_name ./models/EsperBERTo-small
--do_train
--do_eval
--learning_rate 1e-4
--num_train_epochs 5
--save_total_limit 2
--save_steps 2000
--per_gpu_train_batch_size 16
--evaluate_during_training
--seed 42
通常のように、GPUに収まる最大のバッチサイズを選択してください。
🔥🔥🔥 トレーニングを開始しましょう!! 🔥🔥🔥
ここでは、特定のハイパーパラメータのテンソルボードをチェックできます:
デフォルトでは、例のスクリプトはTensorboard形式でログインします。
runs/
に保存されます。その後、ボードを表示するには、tensorboard dev upload --logdir runs
を実行します。これにより、tensorboard.devがセットアップされ、誰とでもML実験を共有できるようになります。
4. 言語モデルが実際にトレーニングされたかどうかの確認
トレーニングと評価の損失が減少しているかどうかを確認する以外に、言語モデルが興味深いものを学習しているかどうかを確認する最も簡単な方法は、FillMaskPipeline
を使用することです。
パイプラインは、トークナイザーとモデルを簡単にラップしたもので、’fill-mask’の場合、マスクされたトークン(ここでは<mask>
)を含むシーケンスを入力し、最も確率の高い埋め込まれたシーケンスのリストとその確率を返します。
from transformers import pipeline
fill_mask = pipeline(
"fill-mask",
model="./models/EsperBERTo-small",
tokenizer="./models/EsperBERTo-small"
)
# The sun <mask>.
# =>
result = fill_mask("La suno <mask>.")
# {'score': 0.2526160776615143, 'sequence': '<s> La suno brilis.</s>', 'token': 10820}
# {'score': 0.0999930202960968, 'sequence': '<s> La suno lumis.</s>', 'token': 23833}
# {'score': 0.04382849484682083, 'sequence': '<s> La suno brilas.</s>', 'token': 15006}
# {'score': 0.026011141017079353, 'sequence': '<s> La suno falas.</s>', 'token': 7392}
# {'score': 0.016859788447618484, 'sequence': '<s> La suno pasis.</s>', 'token': 4552}
OK、シンプルな構文/文法が機能しています。少し興味深いプロンプトを試してみましょう:
fill_mask("Jen la komenco de bela <mask>.")
# This is the beginning of a beautiful <mask>.
# =>
# {
# 'score':0.06502299010753632
# 'sequence':'<s> Jen la komenco de bela vivo.</s>'
# 'token':1099
# }
# {
# 'score':0.0421181358397007
# 'sequence':'<s> Jen la komenco de bela vespero.</s>'
# 'token':5100
# }
# {
# 'score':0.024884626269340515
# 'sequence':'<s> Jen la komenco de bela laboro.</s>'
# 'token':1570
# }
# {
# 'score':0.02324388362467289
# 'sequence':'<s> Jen la komenco de bela tago.</s>'
# 'token':1688
# }
# {
# 'score':0.020378097891807556
# 'sequence':'<s> Jen la komenco de bela festo.</s>'
# 'token':4580
# }
「美しい日の始まり」、実に!
より複雑なプロンプトでは、言語モデルがより多くの意味知識や、(統計的な)常識的な推論を捉えているかどうかを探ることができます。
5. 下流のタスクで言語モデルを微調整する
今、新しいエスペラント語の言語モデルを、品詞タグ付けの下流タスクで微調整することができます。
前述した通り、エスペラント語は単語の語尾が一般的に文法的な品詞を制約する高度に規則的な言語です。CoNLL-2003形式でフォーマットされた注釈付きエスペラント語POSタグのデータセットを使用して、transformers
のrun_ner.py
スクリプトを使用することができます。
POSタグ付けはNERと同じくトークン分類のタスクであるため、まったく同じスクリプトを使用することができます。
再度、こちらがこの微調整のためのホストされたTensorboardです。GPUごとにバッチサイズ64で3エポックトレーニングします。
トレーニングと評価の損失は収束し、タスクが非常に簡単であるため(言語が規則的であるため)、残余値は非常に小さくなります-それでもエンドツーエンドでトレーニングできるのは楽しいです😃。
今回はTokenClassificationPipeline
を使用しましょう:
from transformers import TokenClassificationPipeline, pipeline
MODEL_PATH = "./models/EsperBERTo-small-pos/"
nlp = pipeline(
"ner",
model=MODEL_PATH,
tokenizer=MODEL_PATH,
)
# またはTokenClassificationPipelineを直接インスタンス化する。
nlp("Mi estas viro kej estas tago varma.")
# {'entity': 'PRON', 'score': 0.9979867339134216, 'word': ' Mi'}
# {'entity': 'VERB', 'score': 0.9683094620704651, 'word': ' estas'}
# {'entity': 'VERB', 'score': 0.9797462821006775, 'word': ' estas'}
# {'entity': 'NOUN', 'score': 0.8509314060211182, 'word': ' tago'}
# {'entity': 'ADJ', 'score': 0.9996201395988464, 'word': ' varma'}
うまくいったようです!🔥
NERのより難しいデータセットについては、@stefan-itさんから、WikiANNのシルバースタンダードデータセットでトレーニングすることをおすすめします。
6. モデルを共有する 🎉
最後に、素晴らしいモデルを持っている場合は、ぜひコミュニティと共有することを考えてください:
- CLIを使用してモデルをアップロードする:
transformers-cli upload
- README.mdのモデルカードを作成し、
model_cards/
以下のリポジトリに追加します。モデルカードには、理想的には以下を含めるべきです:- モデルの説明
- トレーニングパラメータ (データセット、前処理、ハイパーパラメータ)
- 評価結果
- 意図される使用方法と制約
- その他役立つ情報!🤓
タダー!
➡️ あなたのモデルはhttps://huggingface.co/modelsでページを持ち、誰でもAutoModel.from_pretrained("username/model_name")
を使用してロードできます。
異なる言語のモデルを見るには、https://huggingface.co/modelsをチェックしてください。
ありがとうございました!
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles