📋 このステップで学ぶこと
- R-CNN(2014年):物体検出に深層学習を初めて適用
- Fast R-CNN(2015年):RoI PoolingとEnd-to-end学習
- Faster R-CNN(2015年):Region Proposal Network(RPN)
- Anchor Boxの概念と仕組み
- 2段階検出(Two-Stage Detection)の全体像
- Mask R-CNN:インスタンスセグメンテーションへの拡張
- PyTorchでのFaster R-CNN実装
📜 1. R-CNN系モデルの歴史
物体検出の分野は、R-CNN系モデルの登場によって大きく進歩しました。2014年から2017年にかけて、R-CNN → Fast R-CNN → Faster R-CNN → Mask R-CNNと進化し、約100倍の高速化と精度向上を実現しました。
【R-CNN系モデルの進化】
年代 モデル 推論速度 mAP 主な改良点
────────────────────────────────────────────────────────────────
2014年 R-CNN 47秒/枚 58.5% 深層学習を物体検出に適用
2015年 Fast R-CNN 2.3秒/枚 66.0% RoI Pooling導入
2015年 Faster R-CNN 0.2秒/枚 73.2% RPN導入
2017年 Mask R-CNN 0.2秒/枚 39.8%* インスタンスセグメンテーション
(* Mask AP)
進化のポイント:
├─ R-CNN → Fast R-CNN: CNNの実行を1回に(約20倍高速化)
├─ Fast R-CNN → Faster R-CNN: RPNでGPU化(約10倍高速化)
└─ Faster R-CNN → Mask R-CNN: マスク予測を追加
1-1. 2段階検出(Two-Stage Detection)とは
R-CNN系モデルは2段階検出と呼ばれるアプローチを採用しています。まず「どこに物体がありそうか」を提案し、次に「それが何か」を分類します。
💡 2段階検出の仕組み
Stage 1: Region Proposal(領域提案)
「物体がありそうな場所」を数千個提案する。
精度より数を優先(漏れなく候補を出す)。
Stage 2: Classification & Regression(分類・回帰)
各候補が「何であるか」を分類する。
Bounding Boxの位置を微調整する。
【2段階検出の流れ】
入力画像
│
↓
┌─────────────────────────────────────┐
│ Stage 1: Region Proposal │
│ │
│ “物体がありそうな場所を提案” │
│ → 約2,000個の候補領域 │
│ │
│ R-CNN/Fast R-CNN: Selective Search│
│ Faster R-CNN: RPN(ニューラルネット)│
└─────────────────────────────────────┘
│
↓ 候補領域(Region Proposals)
│
┌─────────────────────────────────────┐
│ Stage 2: Classification & Regression│
│ │
│ 各候補について: │
│ ├─ 何のクラスか?(分類) │
│ └─ 正確な位置は?(Bbox回帰) │
└─────────────────────────────────────┘
│
↓
最終結果: [(クラス, Box, スコア), …]
🎯 2. R-CNN(2014年)- 物体検出の革命
R-CNN(Regions with CNN features)は、物体検出に深層学習を初めて成功させたモデルです。それまでの手作り特徴量(HOG、SIFTなど)に比べて、精度が大幅に向上しました。
2-1. R-CNNの核心アイデア
💡 R-CNNの発想
「画像分類で大成功したCNNを、物体検出にも使えないか?」
アイデア:
1. 画像から「物体がありそうな領域」を約2,000個切り出す
2. 各領域にCNNを適用して特徴を抽出
3. その特徴を使って分類する
成果:
PASCAL VOC 2012でmAP 35.1%(従来)→ 58.5%(R-CNN)
約23%の大幅な精度向上!
2-2. R-CNNのアーキテクチャ
【R-CNNの処理フロー】
入力画像
│
↓
┌─────────────────────────────────────────────────────┐
│ Step 1: Region Proposal(領域提案) │
│ │
│ Selective Searchアルゴリズムを使用 │
│ ・色、テクスチャ、サイズの類似性で領域を統合 │
│ ・約2,000個の候補領域を生成 │
│ │
│ 出力: 約2,000個のBounding Box │
└─────────────────────────────────────────────────────┘
│
↓ 約2,000個の候補領域
│
┌─────────────────────────────────────────────────────┐
│ Step 2: Feature Extraction(特徴抽出) │
│ │
│ 各候補領域について: │
│ ├─ 227×227にリサイズ(CNNの入力サイズに合わせる)│
│ └─ AlexNet(ImageNet事前学習済み)で特徴抽出 │
│ │
│ 出力: 約2,000個の4096次元特徴ベクトル │
│ │
│ ⚠️ 問題: CNNを約2,000回実行 → 非常に遅い! │
└─────────────────────────────────────────────────────┘
│
↓ 約2,000個の特徴ベクトル
│
┌─────────────────────────────────────────────────────┐
│ Step 3: Classification(分類) │
│ │
│ 各クラスごとにSVM(サポートベクターマシン)を訓練 │
│ 20クラス → 20個のSVM │
│ │
│ 出力: 各候補のクラススコア │
└─────────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────┐
│ Step 4: Bounding Box Regression(位置調整) │
│ │
│ 候補領域の位置を微調整 │
│ 回帰モデルで(Δx, Δy, Δw, Δh)を予測 │
└─────────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────┐
│ Step 5: Non-Maximum Suppression(NMS) │
│ │
│ 重複する検出を除去 │
└─────────────────────────────────────────────────────┘
│
↓
最終結果: [(クラス, Box, スコア), …]
2-3. R-CNNの問題点
❌ R-CNNの問題点
1. 非常に遅い
約2,000個の領域それぞれにCNNを実行 → 1画像あたり47秒(GPU)
リアルタイム処理には全く使えない
2. 学習が複雑(3段階)
① CNN(特徴抽出)
② SVM(分類)
③ Bounding Box回帰
→ それぞれ別々に学習する必要がある
3. メモリ消費が大きい
特徴ベクトルをディスクに保存する必要がある
⚡ 3. Fast R-CNN(2015年)- RoI Pooling導入
Fast R-CNNは、R-CNNの「遅さ」を解決するためにRoI Poolingを導入しました。画像全体を1回だけCNNに通すことで、約20倍の高速化を実現しました。
3-1. Fast R-CNNの核心アイデア
💡 Fast R-CNNの発想
R-CNNの問題:
約2,000個の領域それぞれにCNNを実行 → 同じ画像を約2,000回処理(無駄!)
Fast R-CNNの解決策:
1. 画像全体に1回だけCNNを適用
2. 特徴マップから必要な部分を切り出す(RoI Pooling)
3. 切り出した部分だけを分類・回帰
3-2. RoI Pooling(Region of Interest Pooling)
RoI Poolingは、特徴マップから任意のサイズの領域を切り出し、固定サイズ(通常7×7)に変換する処理です。
【RoI Poolingの仕組み】
■ 問題: 候補領域のサイズがバラバラ
例: 100×50, 200×300, 80×80 など
→ 全結合層に入力するには固定サイズが必要
■ 解決: RoI Poolingで固定サイズに変換
処理の流れ:
┌────────────────────────────────────────────────────┐
│ 入力画像(800×600) │
│ │
│ ┌─────┐ ┌─────────┐ │
│ │犬 │ │ 猫 │ │
│ └─────┘ └─────────┘ │
└────────────────────────────────────────────────────┘
↓ CNN(1回だけ)
┌────────────────────────────────────────────────────┐
│ 特徴マップ(50×38×2048) │
│ │
│ ┌───┐ ┌─────┐ ← 候補領域を │
│ │ │ │ │ 特徴マップ上に │
│ └───┘ └─────┘ 投影 │
└────────────────────────────────────────────────────┘
↓ RoI Pooling
┌─────────────────┐ ┌─────────────────┐
│ 7×7×2048 │ │ 7×7×2048 │
│(犬の特徴) │ │(猫の特徴) │
└─────────────────┘ └─────────────────┘
↓ 全結合層
分類 + Bbox回帰
3-3. RoI Poolingの詳細
【RoI Poolingの具体的な処理】
例: 特徴マップ上の領域 10×14 を 7×7 に変換
Step 1: 領域を7×7のグリッドに分割
10 ÷ 7 ≈ 1.4 → 各セルの幅は1〜2ピクセル
14 ÷ 7 = 2 → 各セルの高さは2ピクセル
┌──┬──┬──┬──┬──┬──┬──┐
│ │ │ │ │ │ │ │ ← 各セルは
├──┼──┼──┼──┼──┼──┼──┤ 約1.4×2の
│ │ │ │ │ │ │ │ 大きさ
├──┼──┼──┼──┼──┼──┼──┤
: : : : : : : :
└──┴──┴──┴──┴──┴──┴──┘
Step 2: 各セル内で最大値を取る(Max Pooling)
→ 7×7の固定サイズ出力
■ 注意点:
座標を整数に丸めるため、若干の位置ずれが発生
→ Mask R-CNNのRoIAlignで改善
3-4. Fast R-CNNのアーキテクチャ
【Fast R-CNNの処理フロー】
入力画像
│
├───────────────────────────────────┐
↓ ↓
┌────────────────────┐ ┌────────────────────────┐
│ CNN(1回だけ実行) │ │ Selective Search │
│ │ │ (領域提案) │
│ VGG-16など │ │ 約2,000個の候補 │
└────────────────────┘ └────────────────────────┘
│ │
↓ 特徴マップ ↓ 候補領域
│ │
└───────────────┬───────────────────┘
│
↓
┌─────────────────────────────────────────────────────┐
│ RoI Pooling │
│ │
│ 各候補領域を特徴マップから切り出し │
│ 7×7の固定サイズに変換 │
└─────────────────────────────────────────────────────┘
│
↓ 約2,000個の7×7特徴
│
┌─────────────────────────────────────────────────────┐
│ 全結合層(FC) │
│ │
│ FC1 → FC2 → 分岐 │
│ ├─ 分類層(クラス数+1) │
│ └─ Bbox回帰層(4×クラス数) │
└─────────────────────────────────────────────────────┘
│
↓
最終結果(NMS後)
3-5. Fast R-CNNの損失関数
【Fast R-CNNの損失関数】
L = L_cls + λ × L_bbox
■ L_cls(分類損失):
Cross Entropy Loss
「この領域は何のクラスか?」を学習
■ L_bbox(Bounding Box回帰損失):
Smooth L1 Loss
「Bounding Boxをどれだけ調整するか?」を学習
■ λ: バランス係数(通常1.0)
─────────────────────────────────────────────────
【Smooth L1 Lossとは】
smooth_L1(x) =
│ 0.5 × x² (|x| < 1のとき)
│ |x| - 0.5 (|x| ≥ 1のとき)
特徴:
・|x|が小さいとき: L2 Loss(滑らかで微分可能)
・|x|が大きいとき: L1 Loss(外れ値に頑健)
なぜ使う?:
・L2 Lossは外れ値に敏感すぎる
・L1 Lossは原点で微分不可能
・Smooth L1はその両方の良いところを取る
3-6. Fast R-CNNの性能比較
| 指標 |
R-CNN |
Fast R-CNN |
| CNNの実行回数 |
約2,000回 |
1回 |
| 訓練時間 |
84時間 |
9.5時間 |
| 推論時間(1画像) |
47秒 |
2.3秒 |
| mAP(VOC2012) |
58.5% |
66.0% |
| 学習方法 |
3段階(別々) |
End-to-end |
❌ Fast R-CNNの残る問題
Region Proposal(Selective Search)がボトルネック
・Selective Searchに約2秒かかる
・CPUで動作(GPUを活用できない)
・全体の処理時間の大部分を占める
→ Faster R-CNNで解決!
🚀 4. Faster R-CNN(2015年)- RPN導入
Faster R-CNNは、Selective SearchをRegion Proposal Network(RPN)に置き換えることで、完全にニューラルネットワークベースの物体検出を実現しました。
4-1. Faster R-CNNの核心アイデア
💡 Faster R-CNNの発想
Fast R-CNNの問題:
Region Proposal(Selective Search)がCPUで遅い → ボトルネック
Faster R-CNNの解決策:
Region Proposal Network(RPN)を導入
・ニューラルネットワークで領域提案
・GPUで高速実行
・特徴マップを共有(効率的)
結果:
・約10倍高速化(Fast R-CNN比)
・完全にEnd-to-end(入力から出力まで全てニューラルネット)
4-2. Region Proposal Network(RPN)
RPNは、特徴マップから「物体がありそうな場所」を提案するネットワークです。Anchor Boxという事前定義されたBoxを基準に、物体の位置を予測します。
【RPNの仕組み】
入力: 特徴マップ(H×W×C)
│
↓
┌─────────────────────────────────────────────────────┐
│ 3×3 Convolution(チャンネル数を512に) │
│ │
│ 特徴マップの各位置で周辺情報を集約 │
└─────────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────┐
│ 各位置にk個のAnchor Boxを配置 │
│ │
│ 例: k=9(3スケール × 3アスペクト比) │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ ┌───┐ ┌─────┐ ┌───────┐ │ │
│ │ │128│ │ 256 │ │ 512 │ 各スケール│ │
│ │ │× │ │ × │ │ × │ │ │
│ │ │128│ │ 256 │ │ 512 │ │ │
│ │ └───┘ └─────┘ └───────┘ │ │
│ │ │ │
│ │ 各スケールで3つのアスペクト比: │ │
│ │ 1:1, 1:2, 2:1 │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
│
├──────────────────┬────────────────────────────────┐
↓ ↓ │
┌──────────────┐ ┌──────────────────────────────┐ │
│ 分類ヘッド │ │ 回帰ヘッド │ │
│ 1×1 Conv │ │ 1×1 Conv │ │
│ (2k ch) │ │ (4k ch) │ │
│ │ │ │ │
│ 各Anchorが │ │ 各Anchorの │ │
│ 物体か背景か │ │ 調整量(Δx,Δy,Δw,Δh) │ │
└──────────────┘ └──────────────────────────────┘ │
│ │ │
↓ ↓ │
スコア(物体らしさ) Bounding Box調整量 │
│ │ │
└──────────────────┴────────────────────────────────┘
│
↓ NMS(IoU閾値=0.7)
│
Region Proposals(約2,000個)
4-3. Anchor Box(アンカーボックス)
Anchor Boxは、特徴マップの各位置に配置する「参照用のBox」です。実際の物体のサイズや形状を直接予測するのではなく、Anchorからの調整量を予測します。
【Anchor Boxの考え方】
■ なぜAnchor Boxを使う?
直接予測する場合:
「この位置に幅200、高さ300のBoxがある」
→ 予測値の範囲が広い(学習が難しい)
Anchor Box + 調整量:
「この位置のAnchor(256×256)を、
幅を0.8倍、高さを1.2倍に調整」
→ 予測値の範囲が狭い(学習が簡単)
─────────────────────────────────────────────────
■ Anchor Boxの例(Faster R-CNN標準設定)
特徴マップサイズ: 50×50(入力画像800×800の場合)
各位置に9個のAnchor:
スケール: 128², 256², 512²(面積)
アスペクト比: 1:1, 1:2, 2:1
┌─────────┬──────────────────────────────────────┐
│スケール │ 1:1 │ 1:2 │ 2:1 │
├─────────┼───────────┼───────────┼────────────┤
│ 128² │ 128×128 │ 91×181 │ 181×91 │
│ 256² │ 256×256 │ 181×362 │ 362×181 │
│ 512² │ 512×512 │ 362×724 │ 724×362 │
└─────────┴───────────┴───────────┴────────────┘
合計Anchor数: 50×50×9 = 22,500個
─────────────────────────────────────────────────
■ Anchor Boxの調整
予測する値(4つ):
tx = (x – x_a) / w_a … 中心xの調整
ty = (y – y_a) / h_a … 中心yの調整
tw = log(w / w_a) … 幅の調整
th = log(h / h_a) … 高さの調整
(x_a, y_a, w_a, h_a): Anchorの座標
(x, y, w, h): 予測したBoxの座標
4-4. Faster R-CNNの全体アーキテクチャ
【Faster R-CNNの処理フロー(詳細)】
入力画像(800×600×3)
│
↓
┌─────────────────────────────────────────────────────┐
│ バックボーンCNN(ResNet-50、VGG-16など) │
│ │
│ 特徴抽出(画像全体を1回処理) │
│ │
│ 出力: 特徴マップ(50×38×2048) │
└─────────────────────────────────────────────────────┘
│
│ 特徴マップ(共有)
│
├───────────────────────────────────────────────────┐
↓ ↓
┌─────────────────────────────┐ ┌─────────────────────────────┐
│ RPN(Region Proposal Network)│ │ 検出ヘッド(後で使用) │
│ │ │ │
│ 3×3 Conv → 分岐: │ │ │
│ ├─ 分類: 物体/背景 │ │ │
│ └─ 回帰: Box調整 │ │ │
│ │ │ │
│ NMS(IoU閾値=0.7) │ │ │
│ │ │ │
│ 出力: 約2,000個のProposals │ │ │
└─────────────────────────────┘ └─────────────────────────────┘
│ │
↓ Region Proposals │
│ │
└───────────────────────────────────────────────────┤
│
┌───────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ RoI Pooling │
│ │
│ 各Proposalを特徴マップから切り出し │
│ 7×7の固定サイズに変換 │
└─────────────────────────────────────────────────────┘
│
↓ 約2,000個の7×7×2048特徴
│
┌─────────────────────────────────────────────────────┐
│ 検出ヘッド │
│ │
│ FC1(4096) → FC2(4096) → 分岐: │
│ ├─ 分類層: 81クラス(80 + 背景) │
│ └─ 回帰層: 4値 × 80クラス │
└─────────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────┐
│ NMS(クラスごと、IoU閾値=0.5) │
│ │
│ 重複する検出を除去 │
└─────────────────────────────────────────────────────┘
│
↓
最終結果: [(クラス, Box, スコア), …]
4-5. Faster R-CNNの損失関数
【Faster R-CNNの損失関数】
L_total = L_rpn + L_rcnn
─────────────────────────────────────────────────
■ L_rpn(RPNの損失)
L_rpn = L_cls_rpn + λ × L_bbox_rpn
L_cls_rpn: 物体/背景の2値分類(Cross Entropy)
・IoU > 0.7 のAnchor → 正例(物体)
・IoU < 0.3 のAnchor → 負例(背景)
・0.3 ≤ IoU ≤ 0.7 → 学習に使わない
L_bbox_rpn: Anchor調整量の回帰(Smooth L1)
・正例のAnchorのみ計算
・Ground Truthへの調整量を予測
─────────────────────────────────────────────────
■ L_rcnn(検出ヘッドの損失)
L_rcnn = L_cls + λ × L_bbox
L_cls: クラス分類(Cross Entropy)
・81クラス(80 + 背景)
L_bbox: Bounding Box調整(Smooth L1)
・背景以外のクラスのみ計算
─────────────────────────────────────────────────
■ 学習のバランス
・RPNでサンプリング: 正例:負例 = 1:1(256個)
・検出ヘッドでサンプリング: 正例:負例 = 1:3(128個)
・正例が少ない場合は負例で補う
4-6. Faster R-CNNの性能比較
| 指標 |
Fast R-CNN |
Faster R-CNN |
| Region Proposal |
Selective Search(CPU) |
RPN(GPU) |
| 推論時間(1画像) |
2.3秒 |
0.2秒 |
| FPS |
0.4 |
5 |
| mAP(VOC2007) |
66.9% |
73.2% |
| 特徴 |
End-to-end(部分的) |
完全にEnd-to-end |
💻 5. Faster R-CNNの実装
PyTorchのtorchvisionには、事前学習済みのFaster R-CNNが用意されています。実際に物体検出を実行してみましょう。
5-1. 事前学習済みモデルでの推論
※ コードが横に長い場合は横スクロールできます
import torch
import torchvision
from torchvision.models.detection import fasterrcnn_resnet50_fpn
from torchvision.models.detection import FasterRCNN_ResNet50_FPN_Weights
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.patches as patches
# ===================================================
# 1. モデルのロード
# ===================================================
# 事前学習済みFaster R-CNN(COCO 80クラス)
# weights=… で最新の重みを指定
weights = FasterRCNN_ResNet50_FPN_Weights.DEFAULT
model = fasterrcnn_resnet50_fpn(weights=weights)
# 評価モードに設定(推論時は必須)
# これにより、Dropout や BatchNorm が推論用の動作になる
model.eval()
# GPUがあれば使用
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
model = model.to(device)
print(f”デバイス: {device}”)
print(“モデルをロードしました”)
# ===================================================
# 2. 画像の前処理
# ===================================================
# 前処理を取得(モデルに付属)
preprocess = weights.transforms()
# 画像を読み込み(PILで)
# 実際に使う場合は ‘test.jpg’ を自分の画像に置き換えてください
# image = Image.open(‘test.jpg’).convert(‘RGB’)
# デモ用のダミー画像(実際は上のコードを使用)
import numpy as np
dummy_array = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
image = Image.fromarray(dummy_array)
# 前処理を適用
image_tensor = preprocess(image)
# バッチ次元を追加(モデルはバッチ入力を期待)
# (C, H, W) → (1, C, H, W)
image_tensor = image_tensor.unsqueeze(0).to(device)
print(f”入力テンソルの形状: {image_tensor.shape}”)
# ===================================================
# 3. 推論の実行
# ===================================================
# 勾配計算を無効化(推論時は不要、メモリ節約)
with torch.no_grad():
# モデルに画像を入力
# 出力は辞書のリスト(バッチサイズ分)
predictions = model(image_tensor)
# 最初の画像の予測結果を取得
pred = predictions[0]
# 予測結果の中身を確認
print(f”\n予測結果のキー: {pred.keys()}”)
print(f”検出数: {len(pred[‘boxes’])}”)
# ===================================================
# 4. 予測結果の取り出し
# ===================================================
# GPUからCPUに移動し、NumPy配列に変換
boxes = pred[‘boxes’].cpu().numpy() # Bounding Box [N, 4]
labels = pred[‘labels’].cpu().numpy() # クラスラベル [N]
scores = pred[‘scores’].cpu().numpy() # 信頼度スコア [N]
print(f”\nBoxes shape: {boxes.shape}”)
print(f”Labels shape: {labels.shape}”)
print(f”Scores shape: {scores.shape}”)
# ===================================================
# 5. 信頼度でフィルタリング
# ===================================================
# 信頼度が閾値以上の検出のみを残す
threshold = 0.5
mask = scores >= threshold
filtered_boxes = boxes[mask]
filtered_labels = labels[mask]
filtered_scores = scores[mask]
print(f”\nフィルタ後の検出数: {len(filtered_boxes)}”)
実行結果:
デバイス: cuda
モデルをロードしました
入力テンソルの形状: torch.Size([1, 3, 480, 640])
予測結果のキー: dict_keys([‘boxes’, ‘labels’, ‘scores’])
検出数: 100
Boxes shape: (100, 4)
Labels shape: (100,)
Scores shape: (100,)
フィルタ後の検出数: 0
5-2. 検出結果の可視化
# ===================================================
# COCOクラス名(81クラス: 背景 + 80カテゴリ)
# ===================================================
COCO_CLASSES = [
‘__background__’, ‘person’, ‘bicycle’, ‘car’, ‘motorcycle’, ‘airplane’,
‘bus’, ‘train’, ‘truck’, ‘boat’, ‘traffic light’, ‘fire hydrant’,
‘N/A’, ‘stop sign’, ‘parking meter’, ‘bench’, ‘bird’, ‘cat’, ‘dog’,
‘horse’, ‘sheep’, ‘cow’, ‘elephant’, ‘bear’, ‘zebra’, ‘giraffe’, ‘N/A’,
‘backpack’, ‘umbrella’, ‘N/A’, ‘N/A’, ‘handbag’, ‘tie’, ‘suitcase’,
‘frisbee’, ‘skis’, ‘snowboard’, ‘sports ball’, ‘kite’, ‘baseball bat’,
‘baseball glove’, ‘skateboard’, ‘surfboard’, ‘tennis racket’, ‘bottle’,
‘N/A’, ‘wine glass’, ‘cup’, ‘fork’, ‘knife’, ‘spoon’, ‘bowl’, ‘banana’,
‘apple’, ‘sandwich’, ‘orange’, ‘broccoli’, ‘carrot’, ‘hot dog’, ‘pizza’,
‘donut’, ‘cake’, ‘chair’, ‘couch’, ‘potted plant’, ‘bed’, ‘N/A’,
‘dining table’, ‘N/A’, ‘N/A’, ‘toilet’, ‘N/A’, ‘tv’, ‘laptop’, ‘mouse’,
‘remote’, ‘keyboard’, ‘cell phone’, ‘microwave’, ‘oven’, ‘toaster’,
‘sink’, ‘refrigerator’, ‘N/A’, ‘book’, ‘clock’, ‘vase’, ‘scissors’,
‘teddy bear’, ‘hair drier’, ‘toothbrush’
]
# ===================================================
# 検出結果を画像に描画する関数
# ===================================================
def visualize_detections(image, boxes, labels, scores, class_names, threshold=0.5):
“””
検出結果を画像に描画する
Args:
image: PIL Image
boxes: Bounding Box [N, 4]
labels: クラスラベル [N]
scores: 信頼度スコア [N]
class_names: クラス名のリスト
threshold: 表示する最小信頼度
“””
# Matplotlibで描画
fig, ax = plt.subplots(1, figsize=(12, 8))
ax.imshow(image)
# 色のリスト(クラスごとに異なる色)
colors = plt.cm.hsv(np.linspace(0, 1, len(class_names)))
for box, label, score in zip(boxes, labels, scores):
# 閾値以下はスキップ
if score < threshold:
continue
# 座標を取得
x1, y1, x2, y2 = box
width = x2 - x1
height = y2 - y1
# クラス名と色を取得
class_name = class_names[label]
color = colors[label % len(colors)]
# Bounding Boxを描画
rect = patches.Rectangle(
(x1, y1), width, height,
linewidth=2,
edgecolor=color,
facecolor='none'
)
ax.add_patch(rect)
# ラベルを描画
label_text = f'{class_name}: {score:.2f}'
ax.text(
x1, y1 - 5,
label_text,
bbox=dict(facecolor=color, alpha=0.7),
fontsize=10,
color='white'
)
ax.axis('off')
plt.tight_layout()
plt.savefig('detection_result.jpg', bbox_inches='tight', dpi=150)
plt.show()
print("結果を保存しました: detection_result.jpg")
# 可視化を実行
# visualize_detections(image, boxes, labels, scores, COCO_CLASSES, threshold=0.5)
5-3. カスタムデータセットでの学習
自分のデータセットでFaster R-CNNを訓練する場合、分類層の出力数を変更する必要があります。
import torch
import torchvision
from torchvision.models.detection import fasterrcnn_resnet50_fpn
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
# ===================================================
# カスタムデータセット用にモデルを修正
# ===================================================
def create_custom_faster_rcnn(num_classes):
“””
カスタムクラス数のFaster R-CNNを作成
Args:
num_classes: クラス数(背景を含む)
例: 犬、猫、鳥の3クラス → num_classes=4
Returns:
修正されたFaster R-CNNモデル
“””
# 事前学習済みモデルをロード
# weights=’DEFAULT’ で最新の重みを使用
model = fasterrcnn_resnet50_fpn(weights=’DEFAULT’)
# 分類層の入力次元を取得
# roi_heads.box_predictor が分類と回帰を行う層
in_features = model.roi_heads.box_predictor.cls_score.in_features
print(f”分類層の入力次元: {in_features}”)
# 分類層を置き換え
# FastRCNNPredictor: 分類(num_classes)+ Bbox回帰(4×num_classes)
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)
return model
# 3クラス + 背景 = 4クラスのモデルを作成
num_classes = 4 # dog, cat, bird + background
model = create_custom_faster_rcnn(num_classes)
print(f”\n新しい分類層:”)
print(model.roi_heads.box_predictor)
実行結果:
分類層の入力次元: 1024
新しい分類層:
FastRCNNPredictor(
(cls_score): Linear(in_features=1024, out_features=4, bias=True)
(bbox_pred): Linear(in_features=1024, out_features=16, bias=True)
)
5-4. 訓練ループの実装
import torch
import torch.optim as optim
from torch.utils.data import DataLoader
# ===================================================
# 訓練の設定
# ===================================================
# デバイス
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
# モデル
num_classes = 4
model = create_custom_faster_rcnn(num_classes)
model = model.to(device)
# オプティマイザ
# 学習可能なパラメータのみを渡す
params = [p for p in model.parameters() if p.requires_grad]
optimizer = optim.SGD(
params,
lr=0.005, # 学習率
momentum=0.9, # モメンタム
weight_decay=0.0005 # 重み減衰(L2正則化)
)
# 学習率スケジューラ
lr_scheduler = optim.lr_scheduler.StepLR(
optimizer,
step_size=3, # 3エポックごとに
gamma=0.1 # 学習率を0.1倍
)
# ===================================================
# DataLoader用のcollate関数
# ===================================================
def collate_fn(batch):
“””
物体検出用のcollate関数
物体検出では画像ごとに物体の数が異なるため、
通常のバッチ処理ができない。
画像とターゲットをタプルのリストとして返す。
“””
return tuple(zip(*batch))
# ===================================================
# 訓練ループ
# ===================================================
def train_one_epoch(model, data_loader, optimizer, device):
“””
1エポックの訓練を実行
Args:
model: Faster R-CNNモデル
data_loader: 訓練データのDataLoader
optimizer: オプティマイザ
device: デバイス(cuda/cpu)
Returns:
平均損失
“””
# 訓練モードに設定
model.train()
total_loss = 0.0
num_batches = 0
for images, targets in data_loader:
# データをデバイスに転送
images = [img.to(device) for img in images]
targets = [{k: v.to(device) for k, v in t.items()} for t in targets]
# 順伝播
# 訓練モードでは、モデルは損失の辞書を返す
loss_dict = model(images, targets)
# 損失を合計
# loss_dict = {‘loss_classifier’: …, ‘loss_box_reg’: …,
# ‘loss_objectness’: …, ‘loss_rpn_box_reg’: …}
losses = sum(loss for loss in loss_dict.values())
# 逆伝播
optimizer.zero_grad()
losses.backward()
optimizer.step()
total_loss += losses.item()
num_batches += 1
return total_loss / num_batches
# ===================================================
# 訓練の実行(データセットがある場合)
# ===================================================
# num_epochs = 10
#
# for epoch in range(num_epochs):
# # 訓練
# avg_loss = train_one_epoch(model, train_loader, optimizer, device)
#
# # 学習率更新
# lr_scheduler.step()
#
# # 結果を表示
# print(f’Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.4f}’)
#
# # モデルの保存
# torch.save(model.state_dict(), ‘faster_rcnn_custom.pth’)
print(“訓練コードを定義しました”)
print(“実際の訓練には、データセットとDataLoaderが必要です”)
🎭 6. Mask R-CNN – インスタンスセグメンテーション
Mask R-CNNは、Faster R-CNNを拡張してインスタンスセグメンテーションを実現したモデルです。Bounding Boxだけでなく、各物体のピクセル単位のマスクも予測します。
6-1. Mask R-CNNの追加機能
💡 Mask R-CNNの拡張点
Faster R-CNNの出力:
・クラス(何の物体か)
・Bounding Box(どこにあるか)
Mask R-CNNの追加出力:
・マスク(物体の正確な形状)← 新規!
【Mask R-CNNのアーキテクチャ】
入力画像
│
↓
バックボーンCNN + FPN(Feature Pyramid Network)
│
↓ 特徴マップ(複数スケール)
│
├─────────────────────────────────────┐
↓ ↓
RPN (特徴マップ共有)
│
↓ Region Proposals
│
RoIAlign(RoI Poolingの改良版)← 重要な改良!
│
├─────────────────┬─────────────────┐
↓ ↓ ↓
分類ヘッド Bbox回帰ヘッド マスクヘッド← 新規!
│ │ │
│ │ │
↓ ↓ ↓
クラス 位置 28×28マスク
│ │ │
└─────────────────┴─────────────────┘
│
↓
最終結果: [(クラス, Box, マスク, スコア), …]
6-2. RoIAlign vs RoI Pooling
Mask R-CNNでは、RoI PoolingをRoIAlignに置き換えています。これにより、ピクセル単位の精度が向上しました。
【RoI Poolingの問題】
特徴マップ上の領域: (x=10.7, y=5.3, width=45.2, height=30.8)
RoI Poolingの処理:
1. 座標を整数に丸める
→ (x=11, y=5, width=45, height=31)
2. 7×7に分割してMax Pooling
→ 各セルのサイズ: 45/7 ≈ 6.4 → 6(整数化)
問題点:
・座標の丸めで最大0.5ピクセルの誤差
・分割時の切り捨てで累積誤差
・Bounding Boxには許容できるが、マスクには精度不足
─────────────────────────────────────────────────
【RoIAlignの解決策】
特徴マップ上の領域: (x=10.7, y=5.3, width=45.2, height=30.8)
RoIAlignの処理:
1. 座標をそのまま使用(丸めない)
→ (x=10.7, y=5.3, width=45.2, height=30.8)
2. 7×7の各セルの中心点を計算
→ 浮動小数点で正確に計算
3. 各中心点でBilinear補間
→ 周囲4点から正確な値を補間
利点:
・位置ずれなし
・ピクセル単位の精度
・マスク予測の精度が大幅に向上
実験結果:
・RoI Pooling: Mask AP 30.3%
・RoIAlign: Mask AP 35.7%
→ +5.4%の精度向上!
6-3. Mask R-CNNの実装
import torch
import torchvision
from torchvision.models.detection import maskrcnn_resnet50_fpn
from torchvision.models.detection import MaskRCNN_ResNet50_FPN_Weights
# ===================================================
# Mask R-CNNのロードと推論
# ===================================================
# 事前学習済みMask R-CNN(COCO)
weights = MaskRCNN_ResNet50_FPN_Weights.DEFAULT
model = maskrcnn_resnet50_fpn(weights=weights)
model.eval()
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
model = model.to(device)
print(“Mask R-CNNをロードしました”)
# 推論(image_tensorは前と同じ)
# with torch.no_grad():
# predictions = model(image_tensor)
# pred = predictions[0]
# Mask R-CNNの出力
# ・boxes: Bounding Box [N, 4]
# ・labels: クラスラベル [N]
# ・scores: 信頼度スコア [N]
# ・masks: マスク [N, 1, H, W](0〜1の確率)
# masks = pred[‘masks’].cpu().numpy()
# print(f”マスクの形状: {masks.shape}”)
# マスクを2値化(閾値0.5)
# binary_mask = masks[0, 0] > 0.5
print(“\nMask R-CNNの出力:”)
print(“・boxes: Bounding Box [N, 4]”)
print(“・labels: クラスラベル [N]”)
print(“・scores: 信頼度スコア [N]”)
print(“・masks: マスク [N, 1, H, W]”)
実行結果:
Mask R-CNNをロードしました
Mask R-CNNの出力:
・boxes: Bounding Box [N, 4]
・labels: クラスラベル [N]
・scores: 信頼度スコア [N]
・masks: マスク [N, 1, H, W]
📝 練習問題
問題1:R-CNN系モデルの比較(基礎)
R-CNN、Fast R-CNN、Faster R-CNNの主な違いを3つの観点から比較してください。
解答:
【R-CNN系モデルの比較】
R-CNN Fast R-CNN Faster R-CNN
────────────────────────────────────────────────────────────────
1. Region Proposal
方法 Selective Search Selective Search RPN(ニューラルネット)
実行環境 CPU CPU GPU
処理時間 約2秒 約2秒 約10ms
2. CNNの実行
回数 約2,000回 1回 1回
効率 非常に悪い 良い 良い
3. 学習方法
段階 3段階 End-to-end End-to-end
(CNN、SVM、回帰)
────────────────────────────────────────────────────────────────
推論時間 47秒/枚 2.3秒/枚 0.2秒/枚
mAP 58.5% 66.0% 73.2%
進化のポイント:
・R-CNN → Fast R-CNN: RoI Poolingで約20倍高速化
・Fast R-CNN → Faster R-CNN: RPNで約10倍高速化
・合計で約200倍の高速化!
問題2:Anchor Boxの数の計算(中級)
入力画像が800×800で、バックボーンの出力が50×50の特徴マップ、各位置に3スケール×3アスペクト比のAnchor Boxを配置する場合、合計でいくつのAnchor Boxが生成されますか?
解答:
【Anchor Box数の計算】
■ 与えられた情報
・入力画像: 800×800
・特徴マップ: 50×50
・スケール: 3種類
・アスペクト比: 3種類
■ 計算
特徴マップの位置数: 50 × 50 = 2,500位置
各位置のAnchor数: 3スケール × 3アスペクト比 = 9個
合計Anchor数: 2,500 × 9 = 22,500個
■ 補足
・これらのAnchorから、RPNが物体らしいものを約2,000個に絞る
・その後、検出ヘッドで分類・回帰を行い、NMSで最終結果を出す
・最終的な検出数は数十〜数百個程度
問題3:Faster R-CNNの損失関数(応用)
Faster R-CNNの損失関数に含まれる4つの損失項を説明してください。
解答:
【Faster R-CNNの4つの損失項】
L_total = L_rpn_cls + L_rpn_bbox + L_rcnn_cls + L_rcnn_bbox
■ RPNの損失(Stage 1)
1. L_rpn_cls(RPN分類損失)
・物体/背景の2値分類
・Cross Entropy Loss
・目的: 「ここに物体がありそうか」を学習
正例: IoU > 0.7 のAnchor
負例: IoU < 0.3 のAnchor
無視: 0.3 ≤ IoU ≤ 0.7
2. L_rpn_bbox(RPN回帰損失)
・Anchorからの調整量
・Smooth L1 Loss
・目的: 「Anchorをどう調整するか」を学習
正例のAnchorのみ計算
■ 検出ヘッドの損失(Stage 2)
3. L_rcnn_cls(RCNN分類損失)
・多クラス分類(81クラス: 80 + 背景)
・Cross Entropy Loss
・目的: 「この物体は何か」を学習
4. L_rcnn_bbox(RCNN回帰損失)
・Bounding Boxの微調整
・Smooth L1 Loss
・目的: 「位置を正確に調整する」を学習
背景以外のクラスのみ計算
問題4:RoI Pooling vs RoIAlignの違い(応用)
RoIAlignがRoI Poolingより優れている理由を、具体的な数値例を挙げて説明してください。
解答:
【RoI Pooling vs RoIAlignの比較】
■ 具体例
特徴マップ上の領域: x=10.7, y=5.3, width=45.2, height=30.8
出力サイズ: 7×7
────────────────────────────────────────────────
【RoI Poolingの処理】
Step 1: 座標を整数に丸める
x: 10.7 → 11 (誤差: 0.3)
y: 5.3 → 5 (誤差: 0.3)
width: 45.2 → 45 (誤差: 0.2)
height: 30.8 → 31 (誤差: 0.2)
Step 2: 7×7に分割
セル幅: 45/7 = 6.43 → 6 or 7(整数に丸め)
セル高: 31/7 = 4.43 → 4 or 5(整数に丸め)
問題点:
・累積誤差: 最大で数ピクセル
・元の領域と対応が不正確
・マスク予測には精度不足
────────────────────────────────────────────────
【RoIAlignの処理】
Step 1: 座標をそのまま使用
x=10.7, y=5.3, width=45.2, height=30.8
Step 2: 各セルの中心点を計算(浮動小数点)
セル(0,0)の中心: (10.7 + 45.2/14, 5.3 + 30.8/14)
= (13.93, 7.50)
Step 3: Bilinear補間
(13.93, 7.50) → 周囲4点から補間
特徴マップの(13,7), (14,7), (13,8), (14,8)を重み付け平均
利点:
・誤差なし(連続値で計算)
・ピクセル単位の精度
・マスク予測の精度が大幅向上
────────────────────────────────────────────────
【性能比較(COCO)】
Mask AP
RoI Pooling: 30.3%
RoIAlign: 35.7%
改善: +5.4%
問題5:カスタムFaster R-CNNの実装(総合)
犬、猫、鳥の3クラスを検出するFaster R-CNNを作成し、訓練ループを書いてください。
解答:
import torch
import torch.optim as optim
from torchvision.models.detection import fasterrcnn_resnet50_fpn
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
from torch.utils.data import DataLoader
# ===================================================
# 1. モデルの作成
# ===================================================
def create_model(num_classes):
“””カスタムクラス数のFaster R-CNNを作成”””
# 事前学習済みモデルをロード
model = fasterrcnn_resnet50_fpn(weights=’DEFAULT’)
# 分類層を置き換え
in_features = model.roi_heads.box_predictor.cls_score.in_features
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)
return model
# 4クラス: 背景(0), 犬(1), 猫(2), 鳥(3)
num_classes = 4
model = create_model(num_classes)
# デバイス
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
model = model.to(device)
# ===================================================
# 2. オプティマイザの設定
# ===================================================
params = [p for p in model.parameters() if p.requires_grad]
optimizer = optim.SGD(params, lr=0.005, momentum=0.9, weight_decay=0.0005)
lr_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1)
# ===================================================
# 3. DataLoader用のcollate関数
# ===================================================
def collate_fn(batch):
return tuple(zip(*batch))
# train_loader = DataLoader(train_dataset, batch_size=2,
# shuffle=True, collate_fn=collate_fn)
# ===================================================
# 4. 訓練ループ
# ===================================================
num_epochs = 10
for epoch in range(num_epochs):
model.train()
epoch_loss = 0.0
# 実際のデータローダーを使用
# for images, targets in train_loader:
# # データをデバイスに転送
# images = [img.to(device) for img in images]
# targets = [{k: v.to(device) for k, v in t.items()} for t in targets]
#
# # 順伝播(訓練時は損失辞書を返す)
# loss_dict = model(images, targets)
# losses = sum(loss for loss in loss_dict.values())
#
# # 逆伝播
# optimizer.zero_grad()
# losses.backward()
# optimizer.step()
#
# epoch_loss += losses.item()
# 学習率更新
lr_scheduler.step()
print(f’Epoch [{epoch+1}/{num_epochs}]’)
# モデルの保存
# torch.save(model.state_dict(), ‘faster_rcnn_dog_cat_bird.pth’)
print(“\n訓練コードの準備完了”)
print(“実際の訓練には、YOLODatasetなどのデータセットが必要です”)
📝 STEP 10 のまとめ
✅ このステップで学んだこと
1. R-CNN(2014年)
・深層学習を物体検出に初めて適用
・Selective Search + CNN + SVM
・遅い(47秒/枚)が、精度は大幅向上
2. Fast R-CNN(2015年)
・RoI Poolingで画像全体を1回処理
・End-to-end学習
・約20倍高速化(2.3秒/枚)
3. Faster R-CNN(2015年)
・Region Proposal Network(RPN)で領域提案もGPU化
・Anchor Boxで様々なサイズ・形状に対応
・約10倍高速化(0.2秒/枚)、完全にEnd-to-end
4. Mask R-CNN(2017年)
・インスタンスセグメンテーションへの拡張
・RoIAlignでピクセル単位の精度
・クラス + Box + マスクを同時に予測
💡 重要ポイント
R-CNN系モデルは「2段階検出」の基礎を築きました。
Stage 1で「どこに」、Stage 2で「何が」を予測する仕組みは、現在でも高精度な物体検出の基本です。
次のSTEP 11では、「YOLOシリーズ」を学びます。YOLOは「1段階検出」で、リアルタイム物体検出を実現した革新的なモデルです。