はじめに
今回は [LLMのFinetuningのための基礎] transformersのAutoClassesの基本を理解する 2 と題しまして、transformersのAutoModelに関して学習したことをまとめました。
今回メインで使用する教材はTransformersのAutoClassesのAPI referenceです。
以下、現段階でのアウトプットの記事のイメージ図です。
「AutoClassの基本的内容を抑えた記事を書いたあとにどのようなことを書くのかはまだ検討中です」と前回は述べていましたが、よく考えるとTrainerも必要だと思い立ったため前回から追加しています。
LLM事前知識・datasetsの基本の理解・AutoTokenizer・AutoConfigの基本を理解するについては下記記事に記載されています。よろしければ是非一読お願いいたします。
おことわり
本記事はあくまでもLLM初学者である私の学習のアウトプットなので不正確・不十分な説明の可能性もあるため参考にされる場合は十分にご注意ください。
目次
- 1. AutoModelとは
- 2. Modelのインスタンス化
- 3. AutoModelForxxx
- 4. Modelの使い方
- 5. おわりに
AutoModelとは
AutoModelはAutoClassesの主要な3つのコンポーネントのAutoConfig
、AutoTokenizer
、AutoModel
の内の1つです。
AutoModelを使用することで指定したMODEL ID もしくは ローカルパス先の設定に関連するモデルのアーキテクチャを読込み、インスタンス化することが可能です。
Modelのインスタンス化
AutoModelのreferenceを見てみましょう。
AutoModelに用意されている関数としてはfrom_config()
とfrom_pretrained()
の2つが用意されています。
ここではこの2つの違いについて見ています。
AutoModel.from_config()
from_config()のreferenceを見てみます。
この関数は主に config
を引数として受け取る ことが分かります。(attn_implementation
も受け取りますが基本的にはデフォルトの値を取ることが多いようです。)
ここでのconfigは前回紹介したPretrainedConfigであることに注意してください。
実際に動かしてみます。
from transformers import AutoConfig, AutoModel
config = AutoConfig.from_pretrained("google-bert/bert-base-cased")
model = AutoModel.from_config(config)
上記を実行すると正常にModelをインスタンス化できます。
しかし注意点としてこのfrom_config()
ではconfigファイルから関連するModel Classのみをロードしています。
つまるところ、モデルの重みはロードしていません。
今回の場合はBertModel
クラスのみをインスタンス化しています。
ここで既に鋭い方はお気づきかと思いますが、from_pretrained()
を使用するとモデルの重みも同時にロードしてインスタンス化できます。
AutoModel.from_pretrained()
from_pretrained()のreferenceを見てみます。
from_pretrained()
にはかなり多くの引数が用意されていることが分かります。
ここでは重要性と覚えやすさの観点から、最初に抑えておくべきだと筆者が個人的に考えた3つ掲載します。
pretrained_model_name_or_path
必須の引数です。
AutoTokenizerと同じでMODEL ID
やローカルの保存先Path
を指定します。
AutoModel.from_pretrained()はそこにある(もしくはロードした)config.jsonから関連するModel Classを特定しインスタンス化します。
config
PretrainedConfigを引数として受け取ります。
例えば一例として、Finetuningなどで作成したデフォルトとは異なるアーキテクチャを持つモデルを使用・ロードしたい場合に、その設定をしたPretrainedConfigを渡します。
from_tf
bool
型を引数として受け取ります。
True
にすることで、TensorFlowで学習されたモデルの重みをPyTorchで活用したいといった場合に使用可能です。
上記3つ以外にも**kwargs(output_attentionsなど)
やrevision
、trust_remote_code
なども重要ですが、先ずは触ってみ動かしてみたいといった初学者にとってはあまり重要ではない可能性が高いため割愛しています。
これらを確認したい場合はreferenceを見たり、実際に動かしてみて挙動の違いをご確認ください。
実際にコードで動作確認してみます。
from transformers import AutoModel
# 先ほどとは違ってconfigを事前に読み込まない。
# config = AutoConfig.from_pretrained("google-bert/bert-base-cased")
model = AutoModel.from_pretrained("google-bert/bert-base-cased")
上記のコードを実行すると正常にモデルがロードされたことが確認できると思います。
また、model.safetensors
というファイルがconfig.json
とは別でロードされたことも確認できたかと思います。
このsafetensors
はHugging Faceが開発したテンソルを安全・高速に保存・読み込む形式です。
今回の場合はここにモデルの重みやバイアスといった情報が格納されており、先ほどみたfrom_config()
では重みなどを読み込んでいないというのがご理解いただけたかと思います。
safetensorsについてより詳しく学ぶために以下2つの記事をご紹介させていただきます。
AutoModelForxxx
ここまではAutoModel
について学習し、まとめてきました。
ですが待ってください。AutoModel
にはAutoConfig
やAutoTokenizer
とは違い、AutoModelForSequenceClassification
のようなAutoModelForxxx
といったコンポーネントが沢山存在しています。
この部分は私の学習時に一番混乱した部分であり、ネットの記事やSNSを見てみても他のAutoClassesと比べて困惑している方が多くいた印象です。
ここではAutoModel
とAutoModelForxxx
の違いについて見ていきます。
AutoModelとAutoModelForxxx
この混乱を解く為に私たちが理解すべきはHugging Faceのモデル構造についてです。
幸いなことにこの部分について詳しくまとめてくれている記事が既にあります。私はこの記事を参考にまとめていきます。
まず、HugginFaceのモデルは基本的にはモデルのベースとなる部分(Body)と特定のタスクに合わせて作成されたヘッド部分(Head)に分かれます。
AutoModelではHeadを持たない状態でModelをロードします。
一方でAutoModelForxxではHead付きでModelをロードします。
AutoModelForxxxでは既に特定のタスクに特化したHeadを持った状態でModelをロードできるため、比較的簡単に使用することが可能です。
一方でAutoModelはHeadを持っていないため出力して返すのは、テキストから抽出した特徴量です。
これを例えば機械学習に回したり、個人のカスタムHeadを通して固有のタスクに合わせることが可能で柔軟です。
しかしこれらはかなり高度なテクニックなため私たち初学者にとっては扱えるようになるためにより多くの経験が必要になるものかと思われます。
実際にロードしてみる
実際にロードしてみます。
ここでは説明のためにあえて先ほどまでと同様のモデル、google-bert/bert-base-casedを使用しています。
また、SequenceClassificationを使いnum_labels
を2として2値分類タスクを想定してみます。
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(
"google-bert/bert-base-cased"
num_labels=2
)
Modelがロードされたあとに次の様な文面が出てくることが確認できます。
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at google-bert/bert-base-cased and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
これは先ほどロードしたモデル(google-bert/bert-base-cased)は元々このタスク(SequenceClassification)のトレーニングを行っておらず、Headのバイアスと重みの情報(classifier.biasとclassifier.weight)を所有していないため適当な値で初期化したことを知らせています。
AutoModelForxxx.from_pretrained()
でロードすると、指定したModelが事前にそのタスクで訓練されていたかに関わらずにHeadを追加してインスタンス化することに注意が必要です。
試しにSequenceClassificationで訓練されたモデル、distilbert/distilbert-base-uncased-finetuned-sst-2-englishで試してみます。
先ほどの様な文面は表示されないはずです。
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(
"distilbert/distilbert-base-uncased-finetuned-sst-2-english",
num_labels=2
)
先ほどの様な文面が出てこないことを確認できました!
注意
Headの重みとバイアスがランダムな値で初期化された文面は出てきませんでしたが、このまま自分のタスクで使用できるわけではありません(正確には使用できるが精度は非常に悪い)。
これは元々訓練されたドメイン知識に偏っているためです。
例えば医療関連のドメインで学習されたModelを使用して医療とは無関連のドメインでタスクを遂行させることはできません。
Modelの使い方
ここまでで、from_config()
、from_pretrained()
を使用してModelクラスのインスタンス化の方法とAutoModel
、AutoModelForxxx
の違いについて見ていきました。
ここからはインスタンス化されたModelの使い方
について見ていきます。
基本的にfrom_pretrained()などでインスタンス化されたモデルたちはPreTrainedModel(PyTorch)
、TFPreTrainedModel(TensorFlow)
、FlaxPreTrainedModel(JAX/Flax)
をベースクラスとしてもつ派生クラスです。
そしてこれら3つのベースクラスはそれぞれtorch.nn.Module
、tf.keras.Model
、flax.linen.Module
を継承しています。(これはソースコードを見ても分かります。)
これらはそれぞれの順伝播メソッド(torch.nn.Module
であればforward()
、TensorFlowのモデルであればcall()
、Flaxのモデルであれば__call__()
)を実装することが期待されており、BartModelを見てみても、それらが実装され__call__
で呼び出せることが分かります。
したがって先ほどまででインタスタンス化したModelクラスはmodel(xxx)
の形式で使用することができます。
基本的な使い方
これらModelクラスがどのような引数を受け取るのかをBartModelを例にとって見てみます。
input_ids
、attention_mask
、decoder_input_ids
などいくつか引数を取ることが確認できますが、まず最初の一歩目としてはinput_idsを渡すことを覚えておきます。
これはModelが予測するために必要な最低限の引数だからです。
実際に動かしてみます。
from transformers import AutoTokenizer, AutoModel
import torch
tokenizer = AutoTokenizer.from_pretrained("facebook/bart-base")
model = AutoModel.from_pretrained("facebook/bart-base")
# return_tensorsはpt(Pytorch)
inputs = tokenizer("Hello, my dog is cute", return_tensors="pt")
# inputsは{'input_ids': tensor([[ 0, 31414, 6, 127, 2335, 16, 11962, 2]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1]])}
# **を付けることで辞書をアンパッキングして引数に入れる。
outputs = model(**inputs)
正常に動いてoutputsには値が入っていることが確認できます。
このoutputsオブジェクトには、モデルの様々な出力が含まれています。例えば、モデルの最終層の隠れ状態(各入力トークンに対応する特徴量ベクトル) を取得したい場合は、次のようにします。
last_hidden_state = outputs.last_hidden_state
この last_hidden_state
は、後続のタスク(例えば、この特徴量を使って別の分類器で分類するなど)に利用できます。
おわりに
今回はAutoModelについて学習していきました。
これで前回までと合わせて、datasetsの基本
、AutoClassesの基本
を学習終えました。
次回は本シリーズの最後であり、Trainerの基本
について学習していきます。
datasets
、AutoClasses
、Trainer
の基本を学び終えると簡単なLLMのFinetuningはご自身で行うことができるようになるかつネット上のFinetuningに関する記事(Kaggleなどのコンペ記事やコードなど)をより鮮明に見て、理解することができるようになるはずです。
何か間違いなどありましたらご指摘お願いいたします。