📋 このステップで学ぶこと
HuggingFace Transformersライブラリの基本
事前学習済みViTモデルを使った画像分類
カスタムデータセットでのファインチューニング
ファインチューニング戦略(全層、分類ヘッドのみ、段階的)
DeiT(Data-efficient Image Transformers)と知識蒸留
Attention Mapの可視化と解釈
層ごとのAttentionパターンの違い
🤗 1. HuggingFace Transformersライブラリ
HuggingFace Transformers は、Transformerモデルを簡単に使えるようにした統一的なライブラリです。NLP(自然言語処理)だけでなく、コンピュータビジョンや音声処理にも対応しています。
1-1. HuggingFace Transformersとは
【HuggingFace Transformersの概要】
■ 何ができるか
1. 事前学習済みモデルを簡単に利用
→ 数行のコードで最先端モデルを使用可能
2. ファインチューニング
→ 自分のデータセットに合わせて微調整
3. モデルの共有
→ Model Hubで世界中のモデルを共有
■ 対応しているViT系モデル
・ViT(Vision Transformer)
・DeiT(Data-efficient Image Transformers)
・Swin Transformer
・BEiT
・CLIP(画像とテキストの同時処理)
・DETR(物体検出)
・SegFormer(セグメンテーション)
など多数
■ なぜHuggingFaceを使うのか
1. 統一API
→ モデルの切り替えが簡単
→ ViT → DeiT → Swin がコード数行の変更で可能
2. 事前学習済みモデル
→ 大規模データで訓練済み
→ すぐに高性能な結果が得られる
3. ドキュメント充実
→ 学習コストが低い
→ 日本語情報も増えている
■ Model Hub
https://huggingface.co/models
世界中の研究者・開発者がモデルを公開
→ 最新のモデルをすぐに試せる
→ 自分のモデルも公開可能
1-2. インストール
Google Colabを使う場合は、以下のコマンドでインストールできます。
# HuggingFace Transformersのインストール
# Google Colabでは最初から入っていることが多いが、
# 最新版にするためにインストールを実行
!pip install transformers torch torchvision
# バージョン確認
import transformers
print(f”transformers version: {transformers.__version__}”)
実行結果:
transformers version: 4.35.0
【インストールオプション】
基本インストール:
pip install transformers
PyTorch込み:
pip install transformers[torch]
画像処理込み:
pip install transformers[torch,vision]
すべての機能:
pip install transformers[all]
※ Google Colabでは pip の代わりに !pip を使用
📦 2. 事前学習済みViTモデルの利用
事前学習済みのViTモデルを使って、画像分類を行う方法を学びます。最も簡単な方法から、詳細な制御ができる方法まで順番に見ていきましょう。
2-1. 最も簡単な方法:pipeline
💡 pipelineとは
HuggingFaceのpipeline は、前処理から推論まですべてを自動で行ってくれる便利な関数です。
たった3行で画像分類ができます。
※ コードが横に長い場合は横スクロールできます
# ===================================================
# 最も簡単な方法:pipeline
# ===================================================
from transformers import pipeline
from PIL import Image
# 1. 画像分類パイプラインを作成
# model: 使用するモデル名(Model Hubから)
# 初回実行時にモデルが自動ダウンロードされる
classifier = pipeline(
“image-classification”, # タスクの種類
model=”google/vit-base-patch16-224″ # モデル名
)
# 2. サンプル画像をダウンロード(テスト用)
import requests
from io import BytesIO
url = “https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg”
response = requests.get(url)
image = Image.open(BytesIO(response.content))
# 3. 推論実行
# top_k: 上位何件の予測を返すか
results = classifier(image, top_k=5)
# 4. 結果を表示
print(“Top 5 Predictions:”)
for result in results:
print(f” {result[‘label’]}: {result[‘score’]*100:.2f}%”)
実行結果:
Top 5 Predictions:
tabby, tabby cat: 45.23%
tiger cat: 28.67%
Egyptian cat: 15.34%
Persian cat: 5.82%
Siamese cat, Siamese: 2.43%
【pipelineの仕組み】
classifier = pipeline(“image-classification”, model=”…”)
この1行で以下が自動的に行われる:
1. モデルのダウンロード
→ Model Hubから事前学習済みモデルを取得
→ 初回のみダウンロード、2回目以降はキャッシュを使用
2. プロセッサ(前処理)のロード
→ 画像のリサイズ(224×224)
→ 正規化(mean、stdで標準化)
3. モデルのロード
→ PyTorchモデルをメモリに読み込み
→ 評価モードに設定
results = classifier(image, top_k=5)
この1行で以下が自動的に行われる:
1. 前処理
→ PILイメージをテンソルに変換
→ リサイズ、正規化
2. 推論
→ モデルに入力
→ ロジットを取得
3. 後処理
→ softmaxで確率に変換
→ 上位k件を抽出
→ クラス名に変換
2-2. 詳細な制御:processor + model
より細かい制御が必要な場合は、processorとmodelを別々にロードします。
# ===================================================
# 詳細な制御:processor + model
# ===================================================
from transformers import ViTImageProcessor, ViTForImageClassification
from PIL import Image
import torch
# ===================================================
# 1. モデルとプロセッサのロード
# ===================================================
# モデル名を指定
# google/vit-base-patch16-224:
# – google: 提供元
# – vit-base: モデルサイズ(base = 86M パラメータ)
# – patch16: パッチサイズ(16×16)
# – 224: 入力画像サイズ(224×224)
model_name = “google/vit-base-patch16-224″
# ViTImageProcessor: 画像の前処理を担当
# – リサイズ(224×224)
# – 正規化(ImageNetの平均・標準偏差)
# – テンソル変換
processor = ViTImageProcessor.from_pretrained(model_name)
# ViTForImageClassification: 分類用ViTモデル
# – Transformer Encoder + 分類ヘッド
# – ImageNetの1000クラス分類用
model = ViTForImageClassification.from_pretrained(model_name)
# 評価モードに設定(Dropout無効化、BatchNorm固定)
model.eval()
# GPUが使える場合はGPUに転送
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
model = model.to(device)
print(f”モデル: {model_name}”)
print(f”クラス数: {model.config.num_labels}”)
print(f”デバイス: {device}”)
実行結果:
モデル: google/vit-base-patch16-224
クラス数: 1000
デバイス: cuda
# ===================================================
# 2. 画像の読み込みと前処理
# ===================================================
# 画像を読み込み(PILイメージとして)
# convert(‘RGB’): 必ずRGB形式に変換(グレースケールやRGBAの場合があるため)
image = Image.open(‘cat.jpg’).convert(‘RGB’)
# 前処理を実行
# processor(): 画像をモデルの入力形式に変換
# – リサイズ: 224×224
# – 正規化: (pixel – mean) / std
# – テンソル化: PILイメージ → PyTorchテンソル
# return_tensors=”pt”: PyTorchテンソルで返す(”tf”ならTensorFlow)
inputs = processor(images=image, return_tensors=”pt”)
# GPUに転送(モデルと同じデバイスに)
inputs = {k: v.to(device) for k, v in inputs.items()}
print(f”入力テンソルのshape: {inputs[‘pixel_values’].shape}”)
print(f”入力テンソルのデバイス: {inputs[‘pixel_values’].device}”)
実行結果:
入力テンソルのshape: torch.Size([1, 3, 224, 224])
入力テンソルのデバイス: cuda:0
# ===================================================
# 3. 推論の実行
# ===================================================
# torch.no_grad(): 勾配計算を無効化
# – 推論時は勾配不要(訓練時のみ必要)
# – メモリ使用量削減、処理速度向上
with torch.no_grad():
# モデルに入力を渡して出力を取得
# **inputs: 辞書を展開して渡す(pixel_values=…)
outputs = model(**inputs)
# outputs.logits: 分類のロジット(softmax前の値)
# shape: (batch_size, num_classes) = (1, 1000)
logits = outputs.logits
# softmaxで確率に変換
# dim=-1: 最後の次元(クラス次元)に対してsoftmax
probs = torch.softmax(logits, dim=-1)
print(f”ロジットのshape: {logits.shape}”)
print(f”確率の合計: {probs.sum().item():.4f}”) # 1.0になるはず
実行結果:
ロジットのshape: torch.Size([1, 1000])
確率の合計: 1.0000
# ===================================================
# 4. 結果の取得と表示
# ===================================================
# Top-5の確率とインデックスを取得
# torch.topk: 上位k個の値とインデックスを返す
top5_prob, top5_idx = torch.topk(probs, 5, dim=-1)
# クラスIDからクラス名への変換辞書を取得
# model.config.id2label: {0: “tench”, 1: “goldfish”, …}
id2label = model.config.id2label
# 結果を表示
print(“\nTop 5 Predictions:”)
print(“-” * 40)
for i in range(5):
# .item(): テンソルから Python の数値を取り出す
class_id = top5_idx[0][i].item()
class_name = id2label[class_id]
prob = top5_prob[0][i].item()
print(f”{i+1}. {class_name}”)
print(f” 確率: {prob*100:.2f}%”)
print(f” クラスID: {class_id}”)
実行結果:
Top 5 Predictions:
—————————————-
1. tabby, tabby cat
確率: 45.23%
クラスID: 281
2. tiger cat
確率: 28.67%
クラスID: 282
3. Egyptian cat
確率: 15.34%
クラスID: 285
4. Persian cat
確率: 5.82%
クラスID: 283
5. Siamese cat, Siamese
確率: 2.43%
クラスID: 284
2-3. 利用可能な事前学習済みモデル
モデル名
事前学習データ
パラメータ数
精度(ImageNet)
vit-base-patch16-224
ImageNet-21K → 1K
86M
81.8%
vit-base-patch32-224
ImageNet-21K → 1K
88M
79.5%
vit-large-patch16-224
ImageNet-21K → 1K
307M
84.0%
vit-huge-patch14-224
ImageNet-21K → 1K
632M
85.4%
【モデル選択の指針】
■ 高精度が必要な場合
model = “google/vit-large-patch16-224”
または
model = “google/vit-huge-patch14-224”
→ 精度優先、計算コストは高い
■ バランス重視(推奨)
model = “google/vit-base-patch16-224”
→ 精度と速度のバランスが良い
→ 多くの実務で十分な性能
■ 高速推論が必要な場合
model = “google/vit-base-patch32-224”
→ パッチサイズが大きい = パッチ数が少ない
→ 計算量が少なく高速
→ 精度はやや低下
■ モデル名の読み方
google/vit-base-patch16-224
│ │ │ │
│ │ │ └─ 入力画像サイズ
│ │ └──────── パッチサイズ
│ └────────────── モデルサイズ
└──────────────────── 提供元
🎓 3. カスタムデータセットでのファインチューニング
事前学習済みモデルを自分のデータセットに合わせて微調整(ファインチューニング)する方法を学びます。
3-1. ファインチューニングとは
💡 ファインチューニングの基本
ファインチューニング とは、大規模データで事前学習したモデルを、
自分の小さなデータセットで追加学習することです。
利点:
・少量データでも高精度(数百〜数千枚で十分)
・学習時間が短い(数十分〜数時間)
・事前学習の知識を活用
【転移学習の流れ】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 事前学習(Pre-training)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
大規模データセット(ImageNet-21K: 1400万枚)
学習される知識:
・エッジ、テクスチャ、形状
・一般的な物体の特徴
・階層的な視覚表現
→ Googleなどが実行済み
→ 私たちはこの結果を使用
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2. ファインチューニング(Fine-tuning)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
自分のデータセット(例: 犬 vs 猫 1000枚)
学習される知識:
・タスク固有の特徴
・犬と猫を区別する特徴
→ 私たちが実行
→ 少量データ・短時間で高精度
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
比喩
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
事前学習 = 大学で一般教養を学ぶ
ファインチューニング = 専門分野を学ぶ
→ 一般教養があるから専門が早く習得できる
3-2. ファインチューニングの3つの戦略
【ファインチューニング戦略の比較】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
戦略1: 全層学習(Full Fine-tuning)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
すべてのパラメータを更新
ViT全体(Transformer Encoder + 分類ヘッド)を学習
推奨データ量: 10,000枚以上
学習率: 2e-5(小さめ)
利点:
・最高精度が期待できる
・タスクに完全に適応
欠点:
・過学習のリスクが高い
・計算コストが高い
・大量データが必要
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
戦略2: 分類ヘッドのみ学習(Linear Probing)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Transformer Encoderを凍結
分類ヘッドのみを学習
推奨データ量: 100〜1,000枚
学習率: 1e-3(大きめ)
利点:
・過学習しにくい
・高速(パラメータ数が少ない)
・少量データで可能
欠点:
・精度に限界がある
・事前学習の特徴をそのまま使用
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
戦略3: 段階的学習(Gradual Unfreezing)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Phase 1: 分類ヘッドのみ(1-2 epochs)
Phase 2: 上位層 + 分類ヘッド(1-2 epochs)
Phase 3: 全層(1-2 epochs)
推奨データ量: 1,000〜10,000枚
利点:
・バランスが良い
・安定した学習
・過学習を防ぎつつ高精度
欠点:
・実装がやや複雑
・学習率の調整が必要
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
データ量と戦略の対応
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
〜500枚: 分類ヘッドのみ
500〜1000枚: 上位層 + 分類ヘッド
1000〜10000枚: 段階的学習
10000枚〜: 全層学習
3-3. 実装:カスタムデータセットでのファインチューニング
犬と猫の分類タスクを例に、ファインチューニングの実装を見ていきましょう。
# ===================================================
# ファインチューニングの実装
# ===================================================
# 必要なライブラリのインポート
from transformers import ViTImageProcessor, ViTForImageClassification
from transformers import TrainingArguments, Trainer
from torch.utils.data import Dataset
import torch
from PIL import Image
import os
from sklearn.metrics import accuracy_score, precision_recall_fscore_support
# ===================================================
# 1. カスタムデータセットクラスの定義
# ===================================================
class CustomImageDataset(Dataset):
“””
カスタム画像データセット
PyTorchのDatasetクラスを継承
__len__と__getitem__を実装する必要がある
“””
def __init__(self, image_paths, labels, processor):
“””
Args:
image_paths: 画像ファイルパスのリスト
labels: 対応するラベルのリスト(0, 1, 2, …)
processor: ViTImageProcessor(前処理用)
“””
self.image_paths = image_paths
self.labels = labels
self.processor = processor
def __len__(self):
“””データセットのサイズを返す”””
return len(self.image_paths)
def __getitem__(self, idx):
“””
idx番目のデータを返す
Returns:
dict: {‘pixel_values’: テンソル, ‘labels’: ラベル}
“””
# 画像を読み込み
image = Image.open(self.image_paths[idx]).convert(‘RGB’)
# 前処理を適用
inputs = self.processor(images=image, return_tensors=”pt”)
# バッチ次元を削除(Trainerが後で追加する)
# (1, 3, 224, 224) → (3, 224, 224)
pixel_values = inputs[‘pixel_values’].squeeze(0)
return {
‘pixel_values’: pixel_values,
‘labels’: torch.tensor(self.labels[idx], dtype=torch.long)
}
# ===================================================
# 2. データの準備
# ===================================================
# 実際のプロジェクトでは、以下のようにファイルパスとラベルを準備
# ここでは例として構造を示す
# データディレクトリの構造:
# data/
# train/
# dog/
# img1.jpg
# img2.jpg
# …
# cat/
# img1.jpg
# img2.jpg
# …
# val/
# dog/
# …
# cat/
# …
def load_dataset_paths(data_dir, class_names):
“””
ディレクトリ構造からファイルパスとラベルを取得
Args:
data_dir: データディレクトリのパス
class_names: クラス名のリスト [‘dog’, ‘cat’]
Returns:
image_paths: 画像パスのリスト
labels: ラベルのリスト
“””
image_paths = []
labels = []
for label_idx, class_name in enumerate(class_names):
class_dir = os.path.join(data_dir, class_name)
if os.path.exists(class_dir):
for filename in os.listdir(class_dir):
if filename.lower().endswith((‘.jpg’, ‘.jpeg’, ‘.png’)):
image_paths.append(os.path.join(class_dir, filename))
labels.append(label_idx)
return image_paths, labels
# クラス設定
class_names = [‘dog’, ‘cat’] # 0: dog, 1: cat
num_classes = len(class_names)
# ラベルマッピング
id2label = {i: name for i, name in enumerate(class_names)}
label2id = {name: i for i, name in enumerate(class_names)}
print(f”クラス数: {num_classes}”)
print(f”id2label: {id2label}”)
print(f”label2id: {label2id}”)
実行結果:
クラス数: 2
id2label: {0: ‘dog’, 1: ‘cat’}
label2id: {‘dog’: 0, ‘cat’: 1}
# ===================================================
# 3. モデルとプロセッサのロード
# ===================================================
model_name = “google/vit-base-patch16-224″
# プロセッサをロード
processor = ViTImageProcessor.from_pretrained(model_name)
# モデルをロード(分類ヘッドをカスタムクラス数に変更)
# ignore_mismatched_sizes=True:
# 事前学習済みモデルは1000クラス、今回は2クラス
# サイズが異なるのでこのオプションで許可
model = ViTForImageClassification.from_pretrained(
model_name,
num_labels=num_classes, # クラス数を指定
id2label=id2label, # ID→ラベル名の辞書
label2id=label2id, # ラベル名→IDの辞書
ignore_mismatched_sizes=True # サイズ不一致を許可
)
print(f”モデルのクラス数: {model.config.num_labels}”)
print(f”パラメータ数: {sum(p.numel() for p in model.parameters()):,}”)
実行結果:
モデルのクラス数: 2
パラメータ数: 85,800,194
# ===================================================
# 4. 訓練設定
# ===================================================
training_args = TrainingArguments(
# 出力ディレクトリ(モデル保存先)
output_dir=”./vit-finetuned”,
# バッチサイズ
# GPUメモリに応じて調整(8, 16, 32など)
per_device_train_batch_size=16,
per_device_eval_batch_size=16,
# エポック数
# 過学習を防ぐため少なめに設定
num_train_epochs=5,
# 評価と保存のタイミング
# “epoch”: 各エポック終了時
# “steps”: 指定ステップごと
evaluation_strategy=”epoch”,
save_strategy=”epoch”,
# 学習率
# ファインチューニングでは小さめに設定
learning_rate=2e-5,
# 正則化(過学習防止)
weight_decay=0.01,
# ロギング設定
logging_dir=”./logs”,
logging_steps=10,
# ベストモデルの保存
load_best_model_at_end=True,
metric_for_best_model=”accuracy”,
# 保存するチェックポイント数の制限
save_total_limit=2,
# Trainerの内部設定
remove_unused_columns=False,
)
print(“訓練設定完了”)
# ===================================================
# 5. 評価指標の定義
# ===================================================
def compute_metrics(eval_pred):
“””
評価指標を計算する関数
Args:
eval_pred: (logits, labels) のタプル
logits: モデルの出力 (N, num_classes)
labels: 正解ラベル (N,)
Returns:
dict: 評価指標の辞書
“””
logits, labels = eval_pred
# ロジットから予測クラスを取得(argmax)
predictions = logits.argmax(axis=-1)
# 正解率
accuracy = accuracy_score(labels, predictions)
# Precision, Recall, F1
# average=’weighted’: クラスの出現頻度で重み付け
precision, recall, f1, _ = precision_recall_fscore_support(
labels, predictions, average=’weighted’
)
return {
‘accuracy’: accuracy,
‘precision’: precision,
‘recall’: recall,
‘f1’: f1
}
print(“評価指標の関数を定義完了”)
# ===================================================
# 6. データセットの作成(実際のデータがある場合)
# ===================================================
# 実際のプロジェクトでは以下のようにデータセットを作成
# train_paths, train_labels = load_dataset_paths(‘data/train’, class_names)
# val_paths, val_labels = load_dataset_paths(‘data/val’, class_names)
#
# train_dataset = CustomImageDataset(train_paths, train_labels, processor)
# val_dataset = CustomImageDataset(val_paths, val_labels, processor)
# ===================================================
# 7. Trainerの作成と訓練
# ===================================================
# trainer = Trainer(
# model=model,
# args=training_args,
# train_dataset=train_dataset,
# eval_dataset=val_dataset,
# compute_metrics=compute_metrics,
# )
#
# # 訓練開始
# print(“訓練を開始します…”)
# trainer.train()
#
# # 最終評価
# print(“\n最終評価:”)
# results = trainer.evaluate()
# for key, value in results.items():
# print(f” {key}: {value:.4f}”)
#
# # モデルを保存
# trainer.save_model(“./vit-finetuned-best”)
# processor.save_pretrained(“./vit-finetuned-best”)
# print(“\nモデルを保存しました: ./vit-finetuned-best”)
3-4. 層の凍結による戦略の実装
# ===================================================
# 戦略別の実装例
# ===================================================
# —————————————————
# 戦略1: 分類ヘッドのみ学習(Linear Probing)
# —————————————————
def freeze_encoder(model):
“””
Transformer Encoderを凍結し、分類ヘッドのみ学習可能にする
少量データ(100〜1000枚)で有効
“””
for name, param in model.named_parameters():
# “classifier” を含まないパラメータを凍結
if “classifier” not in name:
param.requires_grad = False
# 学習可能なパラメータ数を確認
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
total = sum(p.numel() for p in model.parameters())
print(f”学習可能パラメータ: {trainable:,} / {total:,} ({trainable/total*100:.2f}%)”)
# 使用例
# freeze_encoder(model)
実行結果(例):
学習可能パラメータ: 1,538 / 85,800,194 (0.00%)
# —————————————————
# 戦略2: 上位層 + 分類ヘッド
# —————————————————
def freeze_lower_layers(model, num_layers_to_freeze=8):
“””
下位の層を凍結し、上位の層と分類ヘッドのみ学習可能にする
Args:
model: ViTモデル
num_layers_to_freeze: 凍結する層数(0〜11)
中量データ(1000〜5000枚)で有効
“””
for name, param in model.named_parameters():
# 分類ヘッドは常に学習可能
if “classifier” in name:
param.requires_grad = True
continue
# 凍結する層を判定
# ViTの層名は “encoder.layer.0”, “encoder.layer.1″, … の形式
should_freeze = False
for i in range(num_layers_to_freeze):
if f”encoder.layer.{i}.” in name:
should_freeze = True
break
param.requires_grad = not should_freeze
# 学習可能なパラメータ数を確認
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
total = sum(p.numel() for p in model.parameters())
print(f”凍結層数: {num_layers_to_freeze}”)
print(f”学習可能パラメータ: {trainable:,} / {total:,} ({trainable/total*100:.2f}%)”)
# 使用例: 下位8層を凍結、上位4層と分類ヘッドを学習
# freeze_lower_layers(model, num_layers_to_freeze=8)
実行結果(例):
凍結層数: 8
学習可能パラメータ: 28,313,090 / 85,800,194 (33.00%)
# —————————————————
# 戦略3: 段階的学習(Gradual Unfreezing)
# —————————————————
def gradual_unfreeze_training(model, train_dataset, val_dataset, processor):
“””
段階的に層を解凍しながら訓練
Phase 1: 分類ヘッドのみ(2 epochs)
Phase 2: 上位4層 + 分類ヘッド(2 epochs)
Phase 3: 全層(1 epoch)
“””
# ===================
# Phase 1: 分類ヘッドのみ
# ===================
print(“=” * 50)
print(“Phase 1: 分類ヘッドのみ学習”)
print(“=” * 50)
# Encoderを凍結
for name, param in model.named_parameters():
if “classifier” not in name:
param.requires_grad = False
# 訓練(大きめの学習率)
args_phase1 = TrainingArguments(
output_dir=”./phase1″,
num_train_epochs=2,
learning_rate=1e-3, # 大きめ
per_device_train_batch_size=16,
evaluation_strategy=”epoch”,
save_strategy=”no”,
logging_steps=50,
remove_unused_columns=False,
)
trainer = Trainer(
model=model,
args=args_phase1,
train_dataset=train_dataset,
eval_dataset=val_dataset,
compute_metrics=compute_metrics,
)
trainer.train()
# ===================
# Phase 2: 上位4層 + 分類ヘッド
# ===================
print(“\n” + “=” * 50)
print(“Phase 2: 上位4層 + 分類ヘッドを学習”)
print(“=” * 50)
# 上位4層を解凍
for name, param in model.named_parameters():
if “encoder.layer.8” in name or \
“encoder.layer.9” in name or \
“encoder.layer.10” in name or \
“encoder.layer.11” in name or \
“classifier” in name or \
“layernorm” in name.lower():
param.requires_grad = True
# 訓練(中程度の学習率)
args_phase2 = TrainingArguments(
output_dir=”./phase2″,
num_train_epochs=2,
learning_rate=5e-5, # 中程度
per_device_train_batch_size=16,
evaluation_strategy=”epoch”,
save_strategy=”no”,
logging_steps=50,
remove_unused_columns=False,
)
trainer = Trainer(
model=model,
args=args_phase2,
train_dataset=train_dataset,
eval_dataset=val_dataset,
compute_metrics=compute_metrics,
)
trainer.train()
# ===================
# Phase 3: 全層
# ===================
print(“\n” + “=” * 50)
print(“Phase 3: 全層を学習”)
print(“=” * 50)
# 全層を解凍
for param in model.parameters():
param.requires_grad = True
# 訓練(小さめの学習率)
args_phase3 = TrainingArguments(
output_dir=”./phase3″,
num_train_epochs=1,
learning_rate=2e-5, # 小さめ
per_device_train_batch_size=16,
evaluation_strategy=”epoch”,
save_strategy=”epoch”,
load_best_model_at_end=True,
metric_for_best_model=”accuracy”,
logging_steps=50,
remove_unused_columns=False,
)
trainer = Trainer(
model=model,
args=args_phase3,
train_dataset=train_dataset,
eval_dataset=val_dataset,
compute_metrics=compute_metrics,
)
trainer.train()
return model
# 使用例
# model = gradual_unfreeze_training(model, train_dataset, val_dataset, processor)
🎯 4. DeiT(Data-efficient Image Transformers)
DeiT は、少量データでもViTを効果的に学習できるように改良されたモデルです。知識蒸留(Knowledge Distillation)という技術を使います。
4-1. DeiTの概要
💡 DeiTの革新
ViTの問題点:
・大規模データ(数千万〜数億枚)が必要
・ImageNet-1K(130万枚)だけでは性能が低い
DeiTの解決策:
・強力なデータ拡張(RandAugment、MixUp、CutMix)
・知識蒸留(教師モデルから学習)
・蒸留トークン([DISTILL])の導入
【DeiTの成果】
ImageNet-1K(130万枚)のみで訓練した場合:
┌─────────────────────────────────────────────────┐
│ モデル │ Top-1 Accuracy │
├─────────────────────────────────────────────────┤
│ ViT-Base(蒸留なし) │ 76.3% │
│ DeiT-Base(蒸留なし) │ 81.8% (+5.5%) │
│ DeiT-Base(蒸留あり) │ 83.4% (+7.1%) │
└─────────────────────────────────────────────────┘
DeiT vs 大規模事前学習ViT:
┌─────────────────────────────────────────────────┐
│ モデル │ データ量 │ 精度 │
├─────────────────────────────────────────────────┤
│ ViT-Base (ImageNet-21K) │ 1400万枚 │ 81.8%│
│ DeiT-Base(蒸留) │ 130万枚 │ 83.4%│
└─────────────────────────────────────────────────┘
→ 少量データでも大規模事前学習を超える性能!
4-2. 知識蒸留(Knowledge Distillation)
【知識蒸留の仕組み】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
通常の学習
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
入力画像
↓
学生モデル(ViT)
↓
予測確率
↓
損失 = CrossEntropy(予測, 正解ラベル)
正解ラベル(ハードターゲット):
猫: 1.0
犬: 0.0
馬: 0.0
…
→ 「猫か否か」の情報のみ
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
知識蒸留による学習
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
入力画像
↓
┌─────────────────────────────────┐
│ 教師モデル 学生モデル │
│ (CNN) (ViT) │
└─────────────────────────────────┘
↓ ↓
教師の出力 学生の出力
└──────┬──────┘
↓
損失 = α × L_hard + (1-α) × L_soft
L_hard: 正解ラベルとの損失
L_soft: 教師の出力との損失(蒸留損失)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
教師の出力(ソフトターゲット)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
教師モデル(CNN)の出力:
猫: 0.70
犬: 0.15 ← 「犬に似ている」情報
虎: 0.10 ← 「虎にも似ている」情報
馬: 0.03
…
→ クラス間の関係(暗黙的知識)を含む
→ この知識を学生に転移
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
なぜ効果があるのか
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. より多くの情報
正解ラベル: 「猫である」
教師の出力: 「猫である、犬に似ている、虎にも似ている」
→ 豊富な情報から学習
2. CNNのInductive Biasを活用
教師(CNN): 局所性、階層性を持つ
→ その知識をViTに転移
→ ViTの弱点(大量データ必要)を補う
3. 滑らかな学習
ハードターゲット: 0か1
ソフトターゲット: 確率分布
→ 勾配が滑らか、学習が安定
4-3. 蒸留トークン
【DeiTの蒸留トークン】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ViTの構造(復習)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
入力:
[CLS] + パッチ1 + パッチ2 + … + パッチ196
197トークン
出力:
[CLS]’ → 分類ヘッドに入力
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
DeiTの構造
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
入力:
[CLS] + [DISTILL] + パッチ1 + パッチ2 + … + パッチ196
198トークン(1つ増えた)
出力:
[CLS]’ → 正解ラベルとの損失(L_hard)
[DISTILL]’ → 教師モデルとの損失(L_soft)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2つのトークンの役割
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[CLS]トークン:
目的: 正解ラベルを学習
損失: CrossEntropy(予測, 正解ラベル)
→ ハードターゲットからの学習
[DISTILL]トークン:
目的: 教師モデルの出力を学習
損失: KL_Divergence(予測, 教師の出力)
→ ソフトターゲットからの学習
利点:
・2つの異なる目的を分離
・各トークンが専門的に学習
・干渉が少ない
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
推論時の使い方
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
方法1: [CLS]のみ使用
方法2: [DISTILL]のみ使用
方法3: 平均 ([CLS]’ + [DISTILL]’) / 2 ← 最高精度
DeiTでは方法3が推奨される
4-4. DeiTの使用方法
# ===================================================
# DeiTの使用(HuggingFace Transformers)
# ===================================================
from transformers import DeiTImageProcessor, DeiTForImageClassification
from PIL import Image
import torch
# ===================================================
# 1. モデルとプロセッサのロード
# ===================================================
# DeiT-Base(蒸留あり)モデル
# “distilled” が名前に含まれる = 蒸留トークンを持つ
model_name = “facebook/deit-base-distilled-patch16-224″
# プロセッサ(前処理)
processor = DeiTImageProcessor.from_pretrained(model_name)
# モデル
model = DeiTForImageClassification.from_pretrained(model_name)
model.eval()
print(f”モデル: {model_name}”)
print(f”クラス数: {model.config.num_labels}”)
実行結果:
モデル: facebook/deit-base-distilled-patch16-224
クラス数: 1000
# ===================================================
# 2. 推論
# ===================================================
# 画像を読み込み
image = Image.open(‘cat.jpg’).convert(‘RGB’)
# 前処理
inputs = processor(images=image, return_tensors=”pt”)
# 推論
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits
probs = torch.softmax(logits, dim=-1)
# Top-5予測
top5_prob, top5_idx = torch.topk(probs, 5)
print(“\nTop 5 Predictions (DeiT):”)
print(“-” * 40)
for i in range(5):
class_id = top5_idx[0][i].item()
class_name = model.config.id2label[class_id]
prob = top5_prob[0][i].item()
print(f”{i+1}. {class_name}: {prob*100:.2f}%”)
実行結果:
Top 5 Predictions (DeiT):
—————————————-
1. tabby, tabby cat: 52.34%
2. tiger cat: 25.12%
3. Egyptian cat: 12.78%
4. Persian cat: 4.56%
5. Siamese cat, Siamese: 2.89%
🎯 DeiTの意義
実務での利点:
・大規模データセット不要(ImageNet-1Kで十分)
・ViTと同じ推論速度(追加コストなし)
・CNNの知識を活用しつつ、ViTの柔軟性を得る
使い分け:
・データが少ない場合 → DeiT(蒸留あり)推奨
・データが十分ある場合 → ViTでも可
・最高精度を目指す場合 → DeiT(蒸留あり)
🔍 5. Attention Mapの可視化
ViTのAttention Map を可視化することで、モデルが画像のどこに注目しているかを理解できます。
5-1. Attention Mapの抽出
# ===================================================
# Attention Mapの可視化
# ===================================================
import torch
import numpy as np
import matplotlib.pyplot as plt
from transformers import ViTModel, ViTImageProcessor
from PIL import Image
# ===================================================
# 1. モデルの準備(Attention出力を有効化)
# ===================================================
model_name = “google/vit-base-patch16-224”
# プロセッサ
processor = ViTImageProcessor.from_pretrained(model_name)
# ViTModel: 分類ヘッドなしのベースモデル
# output_attentions=True: Attention行列を出力に含める
model = ViTModel.from_pretrained(
model_name,
output_attentions=True # これが重要!
)
model.eval()
print(“Attention出力を有効化したモデルをロードしました”)
# ===================================================
# 2. 画像の前処理と推論
# ===================================================
# 画像を読み込み
image = Image.open(‘cat.jpg’).convert(‘RGB’)
# 前処理
inputs = processor(images=image, return_tensors=”pt”)
# 推論(Attentionも取得)
with torch.no_grad():
outputs = model(**inputs)
# Attentionを取得
# outputs.attentions: タプル(層数分)
# 各要素: (batch_size, num_heads, num_tokens, num_tokens)
# = (1, 12, 197, 197)
attentions = outputs.attentions
print(f”層数: {len(attentions)}”)
print(f”各層のAttention shape: {attentions[0].shape}”)
実行結果:
層数: 12
各層のAttention shape: torch.Size([1, 12, 197, 197])
【Attentionテンソルの構造】
attentions[layer_idx] の shape: (1, 12, 197, 197)
│ │ │ │
│ │ │ └─ どのトークンに注目しているか
│ │ └────── どのトークンが
│ └────────── ヘッド数
└───────────── バッチサイズ
例: attentions[11][0][5][0][10]
= 第12層、ヘッド5、トークン0([CLS])が
トークン10に向けるAttention重み
197トークン:
– トークン0: [CLS]トークン
– トークン1〜196: 14×14 = 196パッチ
5-2. Attention Mapの可視化関数
# ===================================================
# 3. Attention Map可視化関数
# ===================================================
def visualize_attention_map(attentions, layer_idx=-1, head_idx=None):
“””
Attention Mapを可視化
Args:
attentions: モデルのAttention出力
layer_idx: 可視化する層(-1で最終層)
head_idx: 可視化するヘッド(Noneで全ヘッド平均)
Returns:
attn_map: 14×14のAttention Map
“””
# 指定層のAttentionを取得
# shape: (1, num_heads, 197, 197)
attn = attentions[layer_idx][0] # バッチ次元を削除
if head_idx is not None:
# 特定のヘッドのみ
attn = attn[head_idx] # (197, 197)
else:
# 全ヘッドの平均
attn = attn.mean(dim=0) # (197, 197)
# [CLS]トークンが各パッチに向けるAttentionを取得
# attn[0, 1:]: [CLS](インデックス0)から
# パッチ(インデックス1〜196)へのAttention
attn_cls = attn[0, 1:] # (196,)
# 14×14のグリッドに再構成
num_patches = int(np.sqrt(attn_cls.shape[0])) # 14
attn_map = attn_cls.reshape(num_patches, num_patches)
# NumPyに変換
attn_map = attn_map.cpu().numpy()
# 0-1に正規化
attn_map = (attn_map – attn_map.min()) / (attn_map.max() – attn_map.min())
return attn_map
# Attention Mapを取得
attn_map = visualize_attention_map(attentions, layer_idx=-1)
print(f”Attention Map shape: {attn_map.shape}”)
実行結果:
Attention Map shape: (14, 14)
# ===================================================
# 4. 可視化の実行
# ===================================================
# 3つの画像を並べて表示
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# 元画像
axes[0].imshow(image)
axes[0].set_title(“Original Image”, fontsize=14)
axes[0].axis(‘off’)
# Attention Map
im = axes[1].imshow(attn_map, cmap=’viridis’)
axes[1].set_title(“Attention Map (Last Layer)”, fontsize=14)
axes[1].axis(‘off’)
plt.colorbar(im, ax=axes[1], fraction=0.046)
# 重ね合わせ
# 元画像にAttention Mapをオーバーレイ
axes[2].imshow(image)
# リサイズしてオーバーレイ
attn_resized = np.array(Image.fromarray((attn_map * 255).astype(np.uint8)).resize(image.size, Image.BILINEAR)) / 255
axes[2].imshow(attn_resized, cmap=’jet’, alpha=0.5)
axes[2].set_title(“Overlay”, fontsize=14)
axes[2].axis(‘off’)
plt.tight_layout()
plt.savefig(‘attention_visualization.png’, dpi=150, bbox_inches=’tight’)
plt.show()
print(“attention_visualization.png を保存しました”)
5-3. 層ごとのAttention比較
# ===================================================
# 5. 層ごとのAttention比較
# ===================================================
def visualize_attention_across_layers(attentions, image):
“””複数の層のAttentionを比較”””
# 可視化する層(第1, 4, 7, 10, 12層)
layer_indices = [0, 3, 6, 9, 11]
fig, axes = plt.subplots(1, len(layer_indices) + 1, figsize=(20, 4))
# 元画像
axes[0].imshow(image)
axes[0].set_title(“Original”, fontsize=12)
axes[0].axis(‘off’)
# 各層のAttention
for i, layer_idx in enumerate(layer_indices):
attn_map = visualize_attention_map(attentions, layer_idx=layer_idx)
im = axes[i + 1].imshow(attn_map, cmap=’viridis’)
axes[i + 1].set_title(f”Layer {layer_idx + 1}”, fontsize=12)
axes[i + 1].axis(‘off’)
plt.tight_layout()
plt.savefig(‘attention_layers.png’, dpi=150, bbox_inches=’tight’)
plt.show()
print(“attention_layers.png を保存しました”)
# 実行
visualize_attention_across_layers(attentions, image)
5-4. Attention Mapの解釈
【Attention Mapから分かること】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. モデルの注目領域
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
明るい領域 = モデルが強く注目
暗い領域 = モデルがあまり注目していない
例: 猫の画像
明るい: 猫の顔、体
暗い: 背景(木、空)
→ モデルは猫に注目して分類している
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2. 層による違い
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
浅い層(第1-4層):
・Attentionが分散
・画像全体を均等に見る
・低レベル特徴(エッジ、テクスチャ)
視覚的イメージ:
┌──────────┐
│▓▓░░▓▓░░▓▓│ ← 分散したパターン
│░░▓▓░░▓▓░░│
│▓▓░░▓▓░░▓▓│
└──────────┘
中間層(第5-8層):
・物体の形が見え始める
・パーツ(耳、鼻など)に集中
・中レベル特徴
視覚的イメージ:
┌──────────┐
│░░▓▓▓▓░░░░│ ← 物体の形状
│░░▓▓▓▓░░░░│
│░░░░░░░░░░│
└──────────┘
深い層(第9-12層):
・Attentionが集中
・識別に重要な領域のみ
・高レベル特徴(物体全体)
視覚的イメージ:
┌──────────┐
│░░░███░░░░│ ← 猫の顔に集中
│░░░░█░░░░░│
│░░░░░░░░░░│
└──────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
3. CNNとの比較
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CNN(CAM/Grad-CAM):
・クラス活性化マップ
・「どこが分類に重要か」
・後処理で生成
ViT(Attention Map):
・トークン間の関係
・「どことどこが関連しているか」
・モデル内部で直接計算
ViTの利点:
・明示的な関係を可視化
・解釈がより直接的
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4. 実務での活用
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. デバッグ
誤分類の原因を調査
→ Attentionが不適切な領域に向いている?
2. モデルの検証
モデルが適切な特徴を学習しているか確認
→ 医療画像で病変部位に注目している?
3. 説明可能性
ユーザーに予測の根拠を説明
→ 「この領域を見て判断しました」
📝 練習問題
問題1:HuggingFace Transformersの基本(基礎)
HuggingFace Transformersを使って、事前学習済みViTで画像分類を行う最小限のコードを書いてください。
解答を見る
解答:
最も簡単な方法(pipeline):
from transformers import pipeline
from PIL import Image
# 画像分類パイプラインを作成
classifier = pipeline(
“image-classification”,
model=”google/vit-base-patch16-224″
)
# 画像を読み込み
image = Image.open(“cat.jpg”)
# 推論
results = classifier(image, top_k=5)
# 結果を表示
for result in results:
print(f”{result[‘label’]}: {result[‘score’]*100:.2f}%”)
より詳細な制御が必要な場合:
from transformers import ViTImageProcessor, ViTForImageClassification
from PIL import Image
import torch
# プロセッサとモデルをロード
processor = ViTImageProcessor.from_pretrained(“google/vit-base-patch16-224”)
model = ViTForImageClassification.from_pretrained(“google/vit-base-patch16-224”)
model.eval()
# 画像を読み込んで前処理
image = Image.open(“cat.jpg”).convert(‘RGB’)
inputs = processor(images=image, return_tensors=”pt”)
# 推論
with torch.no_grad():
outputs = model(**inputs)
probs = torch.softmax(outputs.logits, dim=-1)
# Top-5予測
top5_prob, top5_idx = torch.topk(probs, 5)
for i in range(5):
class_name = model.config.id2label[top5_idx[0][i].item()]
prob = top5_prob[0][i].item()
print(f”{class_name}: {prob*100:.2f}%”)
問題2:ファインチューニング戦略(中級)
1000枚の犬と猫の画像データセットでViTをファインチューニングする場合、どの戦略が最適か説明してください。
解答を見る
解答:
推奨戦略:段階的学習(Gradual Unfreezing)
理由:
1. データ量(1000枚)との相性
・全層学習: 過学習リスク高い(データ不足)
・分類ヘッドのみ: 精度に限界
・段階的学習: バランスが良い ← 最適
2. 段階的学習の流れ
Phase 1(2 epochs): 分類ヘッドのみ
・学習率: 1e-3(大きめ)
・目的: 新タスク(犬vs猫)に適応
Phase 2(2 epochs): 上位4層 + 分類ヘッド
・学習率: 5e-5(中程度)
・目的: タスク固有の特徴を微調整
Phase 3(1 epoch): 全層
・学習率: 2e-5(小さめ)
・目的: 全体を微調整
3. 期待される精度
・Phase 1後: 85-90%
・Phase 2後: 90-95%
・Phase 3後: 95-98%
データ拡張も重要:
・RandomResizedCrop
・RandomHorizontalFlip
・ColorJitter
・RandomRotation
→ 実質的なデータ量を増やし、過学習を防ぐ
問題3:DeiTの知識蒸留(中級)
DeiTにおける知識蒸留の仕組みと、なぜ教師モデル(CNN)の知識がViT学生モデルの性能向上に寄与するのか説明してください。
解答を見る
解答:
1. 知識蒸留の仕組み
通常の学習:
損失 = CrossEntropy(予測, 正解ラベル)
正解ラベル(ハードターゲット):
猫: 1.0, 犬: 0.0, 馬: 0.0, …
→ 「猫か否か」の情報のみ
知識蒸留:
損失 = α × L_hard + (1-α) × L_soft
L_hard: 正解ラベルとの損失
L_soft: 教師モデルの出力との損失
教師の出力(ソフトターゲット):
猫: 0.70, 犬: 0.15, 虎: 0.10, 馬: 0.03, …
→ 「猫である、犬や虎に似ている」情報を含む
2. CNNの知識がViTに有効な理由
理由1: Inductive Biasの補完
・CNN: 局所性、階層性という強いInductive Bias
・ViT: Inductive Biasが弱い → 大量データ必要
知識蒸留により:
CNNの「画像に特化した知識」をViTに転移
→ ViTがCNNのInductive Biasを間接的に学習
→ 少量データでも高性能
理由2: クラス間関係の学習
教師の出力:
猫: 0.70, 犬: 0.15, 虎: 0.10
→ 「猫は犬や虎に似ている」
学生(ViT)も同様の関係を学習
→ テスト時の誤分類パターンが改善
理由3: より多くの情報
ハードターゲット: 「猫である」
ソフトターゲット: 「猫である、犬に似ている、虎にも似ている」
→ 豊富な情報から学習
3. 蒸留トークンの役割
DeiTの構造:
[CLS] + [DISTILL] + パッチ1 + … + パッチ196
役割分担:
[CLS]: 正解ラベルを学習(ハードターゲット)
[DISTILL]: 教師の出力を学習(ソフトターゲット)
利点:
・2つの目的を独立に最適化
・干渉が少なく、効果的な学習
問題4:Attention Mapの解釈(上級)
ViTのAttention Mapを可視化した際、浅い層と深い層でAttentionパターンがどのように異なるか、具体例を挙げて説明してください。
解答を見る
解答:
例:猫の画像でのAttentionパターン
1. 浅い層(第1-4層)
特徴:
・Attentionが分散
・画像全体を均等に見る
・低レベル特徴(エッジ、テクスチャ、色)
視覚的イメージ:
┌──────────────┐
│ ▓▓░░▓▓░░▓▓░░ │ ← チェッカーボード状
│ ░░▓▓░░▓▓░░▓▓ │ に分散
│ ▓▓░░▓▓░░▓▓░░ │
│ ░░▓▓░░▓▓░░▓▓ │
└──────────────┘
解釈:
・まだ「猫」を認識していない
・局所的なパターンを処理中
・CNNの初期層と類似
2. 中間層(第5-8層)
特徴:
・物体の形状が見え始める
・パーツ(耳、鼻、体)に集中
・中レベル特徴
視覚的イメージ:
┌──────────────┐
│ ░░░░░░░░░░░░ │
│ ░░▓▓▓▓▓▓░░░░ │ ← 猫の輪郭が
│ ░░▓▓▓▓▓▓░░░░ │ 見え始める
│ ░░░░░░░░░░░░ │
└──────────────┘
解釈:
・物体として認識し始める
・背景との分離が進む
・パーツ間の関係を学習中
3. 深い層(第9-12層)
特徴:
・Attentionが強く集中
・識別に重要な領域のみ
・高レベル特徴(物体全体、クラス識別)
視覚的イメージ:
┌──────────────┐
│ ░░░░░░░░░░░░ │
│ ░░░░███░░░░░ │ ← 猫の顔に
│ ░░░░░█░░░░░░ │ 強く集中
│ ░░░░░░░░░░░░ │
└──────────────┘
解釈:
・「猫らしさ」を決定づける領域に注目
・背景は完全に無視
・最終的な分類判断に直結
4. この違いが意味すること
階層的な特徴学習:
浅い層: 低レベル特徴(エッジ、テクスチャ)
中間層: 中レベル特徴(パーツ、形状)
深い層: 高レベル特徴(物体、意味)
CNNとの類似点:
層が深くなるほど抽象的な特徴を学習
→ CNNと同様の階層性
ViTの特徴:
すべての層でグローバルな受容野を持つ
→ 層が深くなるほど「選択的」に注目
📝 STEP 17 のまとめ
✅ このステップで学んだこと
1. HuggingFace Transformers
・pipelineで簡単に推論
・processor + modelで詳細な制御
2. ファインチューニング
・全層学習、分類ヘッドのみ、段階的学習の3戦略
・データ量に応じて戦略を選択
3. DeiT
・知識蒸留で少量データでも高性能
・蒸留トークンでCNNの知識を活用
4. Attention Map
・可視化でモデルの注目領域を理解
・浅い層は分散、深い層は集中
💡 重要ポイント
実務でのViT活用:
・HuggingFace Transformersで簡単に利用可能
・事前学習モデル + 転移学習が基本
・少量データではDeiT(知識蒸留)が有効
次のSTEP 18では、「Swin TransformerとDETR」 を学びます。
階層的なViTと、Transformerベースの物体検出を習得します!