TL;DR:
自前でCNNを一から学習しなくても、ImageNetで学習済みのResNetやEfficientNetの途中層から、優秀な特徴量ベクトルを取得できる!分類やクラスタリング、検索にも使える。
使い方(PyTorch例)
pythonコピーする編集するfrom torchvision import models
import torch.nn as nn
model = models.resnet50(pretrained=True)
model = nn.Sequential(*list(model.children())[:-1]) # 最終FC層の手前まで
features = model(image_tensor) # 出力 shape: [1, 2048, 1, 1]
👉 features.squeeze()
で [2048]
のベクトル完成!
これが便利な理由
メリット | 内容 |
---|---|
💡 再学習不要 | 既にImageNetで訓練済み=高精度な抽象表現が得られる |
🔍 類似画像検索やクラスタリングにも流用可 | cosine類似度で近い画像を見つけられる |
🧠 転移学習の事前確認にも最適 | 学習前に「うまく分離できそうか」検証できる |
覚えておこう!
学習済みCNNは「特徴量抽出器」としても超優秀。
「最終層の1つ手前」こそ、AIの“見ている世界”を取り出す入り口です!
「どの層を“特徴ベクトル”に使う?」を決める 3 ステップ
ステップ | 何をする? | ざっくり理由 |
---|---|---|
1. 目的別の“経験則”を当てはめる | – 画像検索・分類 → 最終 GAP の後(=FC 手前) – セグメンテーション・姿勢推定 → 中〜深層の 2D フィーチャマップ(解像度が残る) – テクスチャ/スタイル解析 → 浅〜中層 | 畳み込み層の深さで「低レベル→高レベル」情報に変わるため |
2. “線形プローブ”で試す | 各層の出力を凍結し、上に1 枚の線形分類器だけ乗せて小データで評価。→ 最も精度が高い層を採用 | 早い+コード数十行で客観比較できる |
3. t-SNE / UMAP で可視化 | 候補層ごとにベクトルを 2 次元に圧縮→クラスタの分離を目視。 | 「似た画像が近いか」を直感確認できる |
実践コード例(PyTorch)
pythonコピーする編集するimport torch, torchvision.models as models
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
model = models.resnet50(weights="DEFAULT").eval()
layers = list(model.children())[:-1] # FC を除外
def extract(layer_idx, x):
with torch.no_grad():
feat = torch.nn.Sequential(*layers[:layer_idx+1])(x)
return feat.flatten(1).cpu().numpy()
best_acc, best_idx = 0, 0
for i in [3, 4, 5, 6]: # conv3_x〜conv5_x + GAP
X_train = extract(i, images_train)
X_test = extract(i, images_test)
clf = LogisticRegression(max_iter=200).fit(X_train, y_train)
acc = accuracy_score(y_test, clf.predict(X_test))
print(f"Layer {i}: {acc:.3f}")
if acc > best_acc:
best_acc, best_idx = acc, i
print("✅ 最適層 =", best_idx)
迷ったらココ!
タイプ | 推奨層 | 備考 |
---|---|---|
汎用特徴(検索・分類) | GAP 後 (2048d) | ResNet系なら layer4 → avgpool |
中解像度マップが欲しい | layer3 出力 (1024ch, 28×28) | 深さと空間情報のバランス◎ |
浅いテクスチャ | layer1 or layer2 | 形より質感を重視 |
まとめ
- 目的ファーストの経験則で候補を絞る
- 線形プローブで簡易ベンチ → 数分で客観比較
- 可視化で直感チェック
この 3 点で「どの層を使うべきか」がデータに基づいて判断できます。
「最終 GAP の後(=FC 手前)」って何? ざっくり図解
┌─ 畳み込み層① ─┐ ← エッジや色 (低レベル特徴)
│ │
├─ 畳み込み層② ─┤ ← 形・パーツ (中レベル)
│ │
├─ 畳み込み層③ ─┤ ← 全体の構造・意味 (高レベル)
│ │
└─ Global Average Pooling (GAP) ─→ ★ココ!
│ (2048 など固定長ベクトル)
▼
Fully Connected (FC) 層
│ ← 「犬」「猫」…各クラスのスコア
Softmax
▼
予測クラス
用語 | 役割 | 例(ResNet-50) |
---|---|---|
GAP (Global Average Pooling) | 画像の**縦×横を全部“平均”**して、各チャンネル(特徴マップ)を 1 個の数値にまとめる処理。 ➡ 空間情報を潰して“意味だけ”を残す。 | avgpool 出力 → 形状 [batch, 2048, 1, 1] |
FC (Fully Connected) 層 | さきほどの 2048 次元ベクトルを受け取り、クラス数(例:1000)だけスコアを出す線形層。 | fc 重み形状 [1000, 2048] |
「最終 GAP の後=FC 手前」
→ GAP でできた 2048 次元ベクトル(ResNet-50/101 系の例)のこと。
- ここには「この画像は何か」を示す濃縮された情報が詰まっている。
- まだクラスに振り分けていないので汎用的(犬・猫・車など共通)。
- ベクトルとして取り出しやすく、検索やクラスタリングに転用しやすい。
つまり “FC に渡す直前のベクトル”=「GAP 後」=「最後から 1 個手前の層」
これを 特徴量 として使うのが鉄板というわけ
アーキテクチャー別の次元数の例
アーキテクチャ | “GAP 後=FC 手前” の次元数 | 備考 |
---|---|---|
ResNet-18 / 34 | 512 | 軽量版のためチャンネルが少ない |
ResNet-50 / 101 / 152 | 2048 | Bottleneck ブロックで幅倍増 |
EfficientNet-B0 | 1280 | Compound scaling によって決定 |
EfficientNet-V2-S | 1536 | V2 系はやや増量 |
MobileNetV3-Large | 960 | モバイル向けで小さめ |
Vision Transformer (ViT-Base, CLS トークン) | 768 | CNN ではないが「CLS 埋め込み」を同様に利用 |
YOLOv8-n Pose (backbone) | 512 | 軽量 YOLO 系バックボーン |
どうやって確認する?
pythonコピーする編集するimport torchvision.models as models
model = models.resnet18(weights="DEFAULT")
print(list(model.children())[-2].out_channels) # → 512
[-2]
がavgpool
直前層(最後の conv 出力)のチャンネル数=ベクトル長。- ViT 系は
model.config.hidden_size
がそのまま次元数です。
実践ポイント
- モデルが軽いほど次元も小さい → メモリ節約になるが情報量も減る。
- 用途に合わせて選択
- 高精度検索やクラスタリング:ResNet50 以上(2048d など)が安定
- モバイル・エッジ:MobileNet, EfficientNet-B0(〜1000d)
- 次元数が足りないと感じたら、中間層を追加で平均したり、PCA/UMAP で圧縮して扱う方法もあります。
まとめ
- ベクトル長は “最後の畳み込みチャンネル数” で決まる(アーキ設計次第)。
- 2048 は一例にすぎないので、使うモデルの仕様を確認して取り出しましょう!