[LLMのFinetuningのための基礎] transformersのAutoClassesの基本を理解する 2

# はじめに
今回は **[LLMのFinetuningのための基礎] transformersのAutoClassesの基本を理解する 2** と題しまして、transformersのAutoModelに関して学習したことをまとめました。

今回メインで使用する教材は[TransformersのAutoClassesのAPI reference](https://huggingface.co/docs/transformers/v4.51.3/ja/model_doc/auto)です。

以下、現段階でのアウトプットの記事のイメージ図です。

```mermaid
flowchart LR
A[LLM事前知識] --> B[datasetsの基本の理解]

subgraph AutoClass
    C[AutoTokenizer・AutoConfigの基本を理解する] --> D[AutoModelの基本を理解する]
end

B --> C
D --> E[Trainer APIの基本を理解する]
```

「AutoClassの基本的内容を抑えた記事を書いたあとにどのようなことを書くのかはまだ検討中です」と前回は述べていましたが、よく考えるとTrainerも必要だと思い立ったため前回から追加しています。

LLM事前知識・datasetsの基本の理解・AutoTokenizer・AutoConfigの基本を理解するについては下記記事に記載されています。よろしければ是非一読お願いいたします。

https://qiita.com/Sora_Yamamoto/items/fd133dcabc25e697e538

https://qiita.com/Sora_Yamamoto/items/c850f03cd56b097be4d6

https://qiita.com/Sora_Yamamoto/items/ea56c8a1f59eaa84fc40


# おことわり
本記事はあくまでもLLM初学者である私の学習のアウトプットなので不正確・不十分な説明の可能性もあるため参考にされる場合は十分にご注意ください。


# 目次

<dl>
  <dt>1. AutoModelとは</dt>  
  <dt>2. Modelのインスタンス化</dt>  
  <dt>3. AutoModelForxxx</dt>
  <dt>4. Modelの使い方</dt>
  <dt>5. おわりに</dt>
</dl>


# AutoModelとは
AutoModelはAutoClassesの主要な3つのコンポーネントの`AutoConfig`、`AutoTokenizer`、`AutoModel`の内の1つです。

AutoModelを使用することで指定したMODEL ID もしくは ローカルパス先の設定に関連するモデルのアーキテクチャを読込み、インスタンス化することが可能です。


# Modelのインスタンス化
[AutoModelのreference](https://huggingface.co/docs/transformers/v4.51.3/ja/model_doc/auto#transformers.AutoModel)を見てみましょう。

AutoModelに用意されている関数としては`from_config()`と`from_pretrained()`の2つが用意されています。
ここではこの2つの違いについて見ています。

## AutoModel.from_config()
[from_config()のreference](https://huggingface.co/docs/transformers/v4.51.3/ja/model_doc/auto#transformers.AutoModel.from_config)を見てみます。

この関数は主に **`config`を引数として受け取る** ことが分かります。（`attn_implementation`も受け取りますが基本的にはデフォルトの値を取ることが多いようです。）
ここでのconfigは前回紹介した[PretrainedConfig](https://huggingface.co/docs/transformers/v4.51.3/ja/main_classes/configuration#transformers.PretrainedConfig)であることに注意してください。

実際に動かしてみます。

```python
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](https://huggingface.co/docs/transformers/v4.51.3/ja/model_doc/auto#transformers.AutoModel.from_pretrained)を見てみます。

`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を見たり、実際に動かしてみて挙動の違いをご確認ください。

実際にコードで動作確認してみます。

```python
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`とは別でロードされたことも確認できたかと思います。

![スクリーンショット 2025-05-11 212115.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/4f2ff053-54f9-462b-a024-68c9156c6fd8.png)

この`safetensors`はHugging Faceが開発したテンソルを安全・高速に保存・読み込む形式です。

今回の場合はここにモデルの重みやバイアスといった情報が格納されており、先ほどみた`from_config()`では重みなどを読み込んでいないというのがご理解いただけたかと思います。

safetensorsについてより詳しく学ぶために以下2つの記事をご紹介させていただきます。

https://huggingface.co/docs/safetensors/index

https://qiita.com/yonaka15/items/e738a33cbbac1ddc0402


# AutoModelForxxx
ここまでは`AutoModel`について学習し、まとめてきました。

ですが待ってください。`AutoModel`には`AutoConfig`や`AutoTokenizer`とは違い、`AutoModelForSequenceClassification`のような`AutoModelForxxx`といったコンポーネントが沢山存在しています。

この部分は私の学習時に一番混乱した部分であり、ネットの記事やSNSを見てみても他のAutoClassesと比べて困惑している方が多くいた印象です。

ここでは`AutoModel`と`AutoModelForxxx`の違いについて見ていきます。

## AutoModelとAutoModelForxxx
この混乱を解く為に私たちが理解すべきは**Hugging Faceのモデル構造**についてです。

幸いなことにこの部分について詳しくまとめてくれている記事が既にあります。私はこの記事を参考にまとめていきます。

https://towardsdatascience.com/adding-custom-layers-on-top-of-a-hugging-face-model-f1ccdfc257bd/

-----

まず、HugginFaceのモデルは基本的にはモデルのベースとなる部分（Body）と特定のタスクに合わせて作成されたヘッド部分（Head）に分かれます。

![スクリーンショット 2025-05-11 221355.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/562854e2-f538-49d6-83f1-ccc73c6e5899.png)

AutoModelでは**Headを持たない状態でModelをロードします。**
一方でAutoModelForxxでは**Head付きでModelをロードします。**

![スクリーンショット 2025-05-11 222312.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/7ccce062-a224-4614-8450-7a7a285ed6e3.png)


AutoModelForxxxでは既に特定のタスクに特化したHeadを持った状態でModelをロードできるため、比較的簡単に使用することが可能です。

一方でAutoModelはHeadを持っていないため出力して返すのは、テキストから抽出した特徴量です。
これを例えば機械学習に回したり、個人のカスタムHeadを通して固有のタスクに合わせることが可能で柔軟です。
しかしこれらはかなり高度なテクニックなため私たち初学者にとっては扱えるようになるために**より多くの経験が必要になるもの**かと思われます。


## 実際にロードしてみる
実際にロードしてみます。
ここでは説明のためにあえて先ほどまでと同様のモデル、**google-bert/bert-base-cased**を使用しています。

また、**SequenceClassification**を使い`num_labels`を2として2値分類タスクを想定してみます。

```python
from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(
            "google-bert/bert-base-cased"
            num_labels=2
            )
```

Modelがロードされたあとに次の様な文面が出てくることが確認できます。

![スクリーンショット 2025-05-11 224111.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/39eee671-b9ff-4d35-bc46-dd0bab493d8b.png)

>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**で試してみます。
先ほどの様な文面は表示されないはずです。

```python
from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(
    "distilbert/distilbert-base-uncased-finetuned-sst-2-english",
    num_labels=2
    )
```

![スクリーンショット 2025-05-11 225049.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/bd08b551-763b-4114-9082-29813a0a83ce.png)

先ほどの様な文面が出てこないことを確認できました！

:::note warn
**注意**
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`を継承しています。（これは[ソースコード](https://github.com/huggingface/transformers/blob/v4.51.3/src/transformers/modeling_utils.py#L1747)を見ても分かります。）

これらはそれぞれの順伝播メソッド（`torch.nn.Module`であれば`forward()`、TensorFlowのモデルであれば`call()`、Flaxのモデルであれば`__call__()`）を実装することが期待されており、[BartModel](https://huggingface.co/docs/transformers/v4.51.3/ja/model_doc/bart#transformers.BartModel)を見てみても、それらが実装され`__call__`で呼び出せることが分かります。

したがって先ほどまででインタスタンス化したModelクラスは`model(xxx)`の形式で使用することができます。

# 基本的な使い方
これらModelクラスがどのような引数を受け取るのかを[BartModel](https://huggingface.co/docs/transformers/v4.51.3/ja/model_doc/bart#transformers.BartModel)を例にとって見てみます。

`input_ids`、`attention_mask`、`decoder_input_ids`などいくつか引数を取ることが確認できますが、まず最初の一歩目としては**input_idsを渡す**ことを覚えておきます。

これはModelが予測するために必要な最低限の引数だからです。

実際に動かしてみます。

```python
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オブジェクトには、モデルの様々な出力が含まれています。例えば、**モデルの最終層の隠れ状態（各入力トークンに対応する特徴量ベクトル）** を取得したい場合は、次のようにします。

```python
last_hidden_state = outputs.last_hidden_state
```

この `last_hidden_state` は、後続のタスク（例えば、この特徴量を使って別の分類器で分類するなど）に利用できます。



# おわりに
今回はAutoModelについて学習していきました。

これで前回までと合わせて、`datasetsの基本`、`AutoClassesの基本`を学習終えました。
次回は本シリーズの最後であり、`Trainerの基本`について学習していきます。

`datasets`、`AutoClasses`、`Trainer`の基本を学び終えると簡単なLLMのFinetuningはご自身で行うことができるようになるかつネット上のFinetuningに関する記事（Kaggleなどのコンペ記事やコードなど）をより鮮明に見て、理解することができるようになるはずです。

何か間違いなどありましたらご指摘お願いいたします。