📋 このステップで学ぶこと
- SSD(Single Shot Detector)の仕組み
- RetinaNet(Focal Loss、クラス不均衡問題の解決)
- EfficientDet(BiFPN、Compound Scaling)
- 評価指標の基礎(TP、FP、FN、IoU)
- Precision(適合率)とRecall(再現率)
- Precision-Recall曲線とAverage Precision(AP)
- mAP(mean Average Precision)の計算方法
- PASCAL VOC評価 vs COCO評価の違い
🗺️ 1. 物体検出モデルの全体像
物体検出モデルは大きく2段階検出と1段階検出に分類されます。これまで学んだR-CNN系(2段階)とYOLO(1段階)に加えて、SSD、RetinaNet、EfficientDetなどの重要なモデルを学びましょう。
【物体検出モデルの系譜】
■ 2段階検出(Two-Stage Detection)
候補領域提案 → 分類・位置調整
R-CNN(2014)→ Fast R-CNN(2015)→ Faster R-CNN(2015)
↓
Mask R-CNN(2017)
特徴: 高精度、やや低速
─────────────────────────────────────────────────────
■ 1段階検出(One-Stage Detection)
画像から直接検出(領域提案なし)
YOLO(2015)→ YOLOv2(2016)→ YOLOv3(2018)→ … → YOLOv8(2023)
↓
SSD(2016)
↓
RetinaNet(2017)← Focal Lossで1段階でも高精度を実現
↓
EfficientDet(2020)← 効率と精度の最適化
特徴: 高速、リアルタイム向け
─────────────────────────────────────────────────────
■ Transformer系(最新)
DETR(2020)→ Deformable DETR → DINO
特徴: End-to-end、NMS不要
📦 2. SSD(Single Shot Detector)
SSD(2016年)は、YOLOと同じ1段階検出ですが、複数の特徴マップから同時に検出することで精度を向上させました。
2-1. SSDの核心アイデア
💡 SSDの核心アイデア
YOLOv1の問題:
最終層の特徴マップ(7×7)のみを使用
→ 小さい物体の検出が苦手
SSDの解決策:
複数の異なるサイズの特徴マップから検出
→ 浅い層(高解像度): 小さい物体
→ 深い層(低解像度): 大きい物体
2-2. SSDのアーキテクチャ
【SSD300のアーキテクチャ】
入力画像(300×300×3)
↓
┌─────────────────────────────────────────────────────┐
│ VGG-16バックボーン(ImageNet事前学習済み) │
│ │
│ Conv1〜Conv5まで使用 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Conv4_3(38×38×512) │
│ → 検出1: 小さい物体用 │
│ → 4個のDefault Box/位置 │
│ → 出力: 38×38×4×(4+21) = 38×38×100 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Conv7/FC7(19×19×1024) │
│ → 検出2: 中小の物体用 │
│ → 6個のDefault Box/位置 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Conv8_2(10×10×512) │
│ → 検出3: 中サイズの物体用 │
│ → 6個のDefault Box/位置 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Conv9_2(5×5×256) │
│ → 検出4: やや大きい物体用 │
│ → 6個のDefault Box/位置 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Conv10_2(3×3×256) │
│ → 検出5: 大きい物体用 │
│ → 4個のDefault Box/位置 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Conv11_2(1×1×256) │
│ → 検出6: 画像全体を占める物体用 │
│ → 4個のDefault Box/位置 │
└─────────────────────────────────────────────────────┘
↓
NMS(Non-Maximum Suppression)
↓
最終検出結果
2-3. Default Box(Anchor Box)
SSDでは、各特徴マップの位置にDefault Box(Anchor Boxと同義)を配置し、そこからの調整量を予測します。
【Default Boxの設計】
■ 各特徴マップでのDefault Box数
特徴マップ サイズ Default Box数 合計
────────────────────────────────────────────────
Conv4_3 38×38 4 5,776
Conv7 19×19 6 2,166
Conv8_2 10×10 6 600
Conv9_2 5×5 6 150
Conv10_2 3×3 4 36
Conv11_2 1×1 4 4
────────────────────────────────────────────────
合計 8,732個
■ アスペクト比の設計
4個の場合: 1:1, 2:1, 1:2, 1:1(別スケール)
6個の場合: 1:1, 2:1, 1:2, 3:1, 1:3, 1:1(別スケール)
■ スケールの計算
s_k = s_min + (s_max – s_min) × (k-1) / (m-1)
・s_min = 0.2(最小スケール)
・s_max = 0.9(最大スケール)
・m = 6(特徴マップの数)
・k = 1, 2, …, 6
例:
・Conv4_3: s = 0.1(特別に小さく設定)
・Conv7: s = 0.2
・Conv8_2: s = 0.38
・Conv9_2: s = 0.54
・Conv10_2: s = 0.72
・Conv11_2: s = 0.9
2-4. SSDの損失関数
【SSDの損失関数】
L = (1/N) × (L_conf + α × L_loc)
N: マッチしたDefault Boxの数
α: バランス係数(通常1)
─────────────────────────────────────────────────────
■ L_conf(信頼度損失)
Softmax Cross Entropy Loss
全クラス(21クラス: 20 + 背景)の分類
L_conf = -Σ log(softmax(c_i^p))
・c_i^p: クラスpの予測スコア
─────────────────────────────────────────────────────
■ L_loc(位置損失)
Smooth L1 Loss
マッチしたDefault Boxのみ計算
L_loc = Σ smooth_L1(l – g)
・l: 予測した調整量 (Δcx, Δcy, Δw, Δh)
・g: 正解の調整量
─────────────────────────────────────────────────────
■ Hard Negative Mining
問題: 背景(負例)が圧倒的に多い
→ 8,732個のうち、物体があるのは数個〜数十個
解決: 損失が大きい負例のみを使用
1. すべての負例の損失を計算
2. 損失が大きい順にソート
3. 正例:負例 = 1:3 になるように選択
例: 正例が10個なら、負例は上位30個を使用
🎯 SSDのまとめ
速度: SSD300で約46fps、SSD512で約19fps
精度: SSD300で74.3 mAP、SSD512で76.8 mAP(VOC2007)
特徴: マルチスケール検出でYOLOv1より高精度、Faster R-CNNより高速
🎯 3. RetinaNet(2017年)- Focal Loss
RetinaNetは、Focal Lossという革新的な損失関数を導入し、1段階検出で初めて2段階検出の精度を超えました。
3-1. クラス不均衡問題
⚠️ 1段階検出のクラス不均衡問題
問題の本質:
1段階検出では、画像全体に大量のAnchor Box(約10万個)を配置
そのうち実際に物体があるのはわずか数個〜数十個
具体例:
・Anchor Box総数: 100,000個
・物体があるBox: 10個
・背景のBox: 99,990個
→ 背景:物体 = 10,000:1 の極端な不均衡!
従来の対策:
・Hard Negative Mining(SSD): 難しい負例のみ使用
・OHEM: オンラインで難しい例を選択
→ でも根本的な解決にはならない
3-2. Focal Lossの仕組み
💡 Focal Lossの核心アイデア
「簡単な例の損失を減らし、難しい例に集中させる」
【Cross Entropy Loss vs Focal Loss】
■ 従来のCross Entropy Loss
CE(p_t) = -log(p_t)
p_t: 正解クラスの予測確率
例:
・p_t = 0.9(簡単な例、よく分類できている)
→ CE = -log(0.9) = 0.105
・p_t = 0.1(難しい例、分類できていない)
→ CE = -log(0.1) = 2.303
─────────────────────────────────────────────────────
■ Focal Loss
FL(p_t) = -(1 – p_t)^γ × log(p_t)
γ(ガンマ): focusing parameter(通常2)
(1 – p_t)^γ: modulating factor(調整係数)
例(γ = 2):
・p_t = 0.9(簡単な例)
→ FL = -(1-0.9)^2 × log(0.9)
= -0.01 × 0.105
= 0.00105
→ CEの約1/100に削減!
・p_t = 0.1(難しい例)
→ FL = -(1-0.1)^2 × log(0.1)
= -0.81 × 2.303
= 1.865
→ CEの約0.8倍(あまり変わらない)
─────────────────────────────────────────────────────
■ 効果の比較表
予測確率 CE Loss FL (γ=2) 削減率
───────────────────────────────────────
0.9 0.105 0.00105 99%
0.8 0.223 0.0089 96%
0.5 0.693 0.173 75%
0.2 1.609 1.029 36%
0.1 2.303 1.865 19%
→ 簡単な例(高確率): 損失を大幅に削減
→ 難しい例(低確率): 損失はあまり変わらない
→ モデルは「難しい例」に集中して学習!
3-3. RetinaNetのアーキテクチャ
【RetinaNetの構造】
入力画像
↓
┌─────────────────────────────────────────────────────┐
│ バックボーン: ResNet-50 / ResNet-101 │
│ │
│ ImageNetで事前学習済み │
│ C3, C4, C5の特徴を出力 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ FPN(Feature Pyramid Network) │
│ │
│ トップダウンで特徴を融合 │
│ P3, P4, P5, P6, P7を出力 │
│ │
│ ・P3: 大きい解像度(小さい物体用) │
│ ・P7: 小さい解像度(大きい物体用) │
└─────────────────────────────────────────────────────┘
↓
├─────────────────────────────────────────────────┐
↓ ↓
┌──────────────────────┐ ┌──────────────────────┐
│ 分類サブネット │ │ 回帰サブネット │
│ │ │ │
│ 4層のConv(256ch) │ │ 4層のConv(256ch) │
│ ↓ │ │ ↓ │
│ Conv(K×A ch) │ │ Conv(4×A ch) │
│ │ │ │
│ K: クラス数 │ │ 4: (dx,dy,dw,dh) │
│ A: Anchor数(9) │ │ A: Anchor数(9) │
│ │ │ │
│ Sigmoid出力 │ │ │
│(各クラス独立) │ │ │
└──────────────────────┘ └──────────────────────┘
↓ ↓
クラス予測 位置調整
└─────────────────────────────────────────────────┘
↓
NMS
↓
最終検出結果
3-4. RetinaNetの革新性
💡 RetinaNetの成果
精度: 39.1 AP(COCO)
→ 1段階検出で初めて2段階検出(Faster R-CNN: 36.2 AP)を超えた!
速度: 約100ms(ResNet-101-FPN)
→ Faster R-CNNより速い
歴史的意義:
・「1段階検出は精度が低い」という常識を覆した
・クラス不均衡問題の根本的な解決策を提示
・Focal Lossは他のタスクにも応用可能
⚡ 4. EfficientDet(2020年)
EfficientDetは、EfficientNetの「Compound Scaling」のアイデアを物体検出に応用し、精度と効率のバランスを最適化しました。
4-1. BiFPN(Bidirectional FPN)
【FPN vs BiFPN】
■ 従来のFPN(Feature Pyramid Network)
トップダウンのみの情報伝播:
P7(深い、低解像度)
↓
P6
↓
P5
↓
P4
↓
P3(浅い、高解像度)
問題: 深い層 → 浅い層の一方向のみ
─────────────────────────────────────────────────────
■ BiFPN(Bidirectional FPN)
双方向の情報伝播 + 学習可能な重み付け:
P7 ──────────────────→ P7′
↘ ↗
P6 ────────────→ P6′
↘ ↗
P5 ──────→ P5′
↘ ↗
P4 ─→ P4′
↘↗
P3′
特徴:
1. トップダウン + ボトムアップの双方向
2. 各ノードで複数の入力を重み付け融合
3. 重みは学習可能(Attention的)
重み付き融合:
O = Σ(w_i × I_i) / (Σ w_i + ε)
w_i: 学習可能な重み
I_i: 入力特徴
ε: 数値安定化用の小さな値
4-2. Compound Scaling
【EfficientDetのCompound Scaling】
EfficientNetと同様に、3つの次元を同時にスケール:
1. バックボーンのサイズ
EfficientNet-B0 → B1 → … → B6
2. BiFPNのチャンネル数と層数
チャンネル: 64 → 88 → … → 384
層数: 3 → 4 → … → 8
3. 入力解像度
512 → 640 → 768 → 896 → 1024 → 1280 → 1536
─────────────────────────────────────────────────────
【スケーリング係数】
φ = 0, 1, 2, 3, 4, 5, 6, 7(D0〜D7に対応)
バックボーン: B_φ
BiFPNチャンネル: W_bifpn = 64 × (1.35^φ)
BiFPN層数: D_bifpn = 3 + φ
入力解像度: R = 512 + φ × 128
例(D3):
・バックボーン: EfficientNet-B3
・BiFPNチャンネル: 160
・BiFPN層数: 6
・入力解像度: 896×896
4-3. EfficientDetのモデルファミリー
| モデル |
バックボーン |
解像度 |
パラメータ |
mAP |
推論時間 |
| D0 |
EfficientNet-B0 |
512 |
3.9M |
33.8 |
6.1ms |
| D1 |
EfficientNet-B1 |
640 |
6.6M |
39.6 |
8.4ms |
| D2 |
EfficientNet-B2 |
768 |
8.1M |
43.0 |
11.8ms |
| D3 |
EfficientNet-B3 |
896 |
12.0M |
45.8 |
17.3ms |
| D4 |
EfficientNet-B4 |
1024 |
20.7M |
49.4 |
32.4ms |
| D7 |
EfficientNet-B6 |
1536 |
51.9M |
52.2 |
95ms |
📊 5. 物体検出モデルの比較
| モデル |
年 |
mAP |
速度 |
特徴 |
| Faster R-CNN |
2015 |
36.2 |
〜200ms |
2段階検出の標準、高精度 |
| SSD300 |
2016 |
25.1 |
〜22ms |
マルチスケール、シンプル |
| YOLOv3 |
2018 |
33.0 |
〜29ms |
高速、マルチスケール |
| RetinaNet |
2017 |
39.1 |
〜100ms |
Focal Loss、1段階で高精度 |
| EfficientDet-D3 |
2020 |
45.8 |
〜17ms |
効率的、BiFPN |
| YOLOv8x |
2023 |
53.9 |
〜40ms |
最新、Anchor-Free |
🎯 モデル選択のガイドライン
リアルタイム性重視(監視カメラ、自動運転):
→ YOLOv8n/s、EfficientDet-D0/D1
精度重視(医療、品質検査):
→ YOLOv8x、EfficientDet-D7、Faster R-CNN
バランス重視(一般用途):
→ YOLOv8m、EfficientDet-D3
リソース制約(エッジデバイス):
→ YOLOv8n、EfficientDet-D0、MobileNet-SSD
📏 6. 評価指標の基礎
物体検出の性能を評価するには、複数の指標を理解する必要があります。まず基本的な概念から学びましょう。
6-1. 検出結果の分類(TP、FP、FN)
【検出結果の4つの分類】
物体検出では、予測と正解の組み合わせを以下のように分類します:
■ True Positive (TP): 正しい検出
・物体があり、かつ正しく検出した
・予測Boxと正解BoxのIoU ≥ 閾値(通常0.5)
■ False Positive (FP): 誤検出
・物体がないのに検出した(背景を物体と誤認)
・または、IoU < 閾値で検出精度が低い
■ False Negative (FN): 見逃し
・物体があるのに検出できなかった
■ True Negative (TN): 通常は使わない
・背景を正しく背景と判定
・物体検出では背景は無数にあるため、計算しない
─────────────────────────────────────────────────────
【具体例】
画像に3つの正解物体があり、モデルが4つの検出を出力した場合:
正解(Ground Truth):
GT1: 猫 at (50, 30, 200, 150)
GT2: 犬 at (300, 100, 450, 250)
GT3: 鳥 at (100, 300, 180, 400)
予測(Predictions):
P1: 猫 at (55, 35, 195, 145) → IoU=0.85 with GT1 → TP
P2: 犬 at (305, 105, 445, 245) → IoU=0.90 with GT2 → TP
P3: 猫 at (400, 50, 500, 100) → 対応するGTなし → FP
P4: 犬 at (80, 280, 150, 350) → IoU=0.15 with GT3 → FP(IoU低い)
結果:
TP = 2(P1, P2)
FP = 2(P3, P4)
FN = 1(GT3は検出されず)
6-2. IoU(Intersection over Union)
【IoUの復習】
IoU = 交差部分の面積 / 和集合の面積
┌───────────┐
│ 予測Box │
┌───┼───┐ │
│ │ ∩ │ │ 交差部分 = ∩ の面積
│ └───┼───────┘
│ │
└───────┘
正解Box
IoU = ∩ / (予測Box + 正解Box – ∩)
─────────────────────────────────────────────────────
【IoU値の解釈】
IoU = 1.0: 完全一致(理論上の最大値)
IoU ≥ 0.75: 非常に良い検出(厳しい基準で合格)
IoU ≥ 0.5: 良い検出(標準的な基準で合格)
IoU < 0.5: 不十分な検出(通常は誤検出扱い)
IoU = 0.0: 全く重なっていない
─────────────────────────────────────────────────────
【IoU閾値の選択】
・PASCAL VOC: IoU ≥ 0.5 を「正解」とみなす
・COCO: IoU = 0.5, 0.55, 0.60, ..., 0.95 の複数閾値で評価
IoU閾値が高いほど:
・より厳しい評価(位置精度が重要)
・TPが減り、FPとFNが増える
・mAPが下がる
6-3. PrecisionとRecall
💡 PrecisionとRecallの定義
Precision(適合率、精度):
「検出したもののうち、正しいものの割合」
Precision = TP / (TP + FP)
Recall(再現率、検出率):
「全ての正解のうち、検出できたものの割合」
Recall = TP / (TP + FN)
【PrecisionとRecallの計算例】
先ほどの例:
TP = 2, FP = 2, FN = 1
Precision = TP / (TP + FP)
= 2 / (2 + 2)
= 2 / 4
= 0.5 (50%)
「検出した4つのうち、2つが正しい」
Recall = TP / (TP + FN)
= 2 / (2 + 1)
= 2 / 3
= 0.667 (66.7%)
「正解3つのうち、2つを検出できた」
─────────────────────────────────────────────────────
【PrecisionとRecallのトレードオフ】
信頼度閾値を上げる(厳しくする):
・低信頼度の検出を除外
・FPが減る → Precisionが上がる
・でもTPも減る可能性 → Recallが下がる
信頼度閾値を下げる(緩くする):
・より多くの検出を許可
・TPが増える可能性 → Recallが上がる
・でもFPも増える → Precisionが下がる
→ PrecisionとRecallはトレードオフの関係!
→ 両方を同時に最大化することは難しい
📈 7. Precision-Recall曲線とAP
7-1. Precision-Recall曲線の作成
【Precision-Recall曲線の作成手順】
Step 1: すべての検出を信頼度順にソート
検出結果(信頼度順):
検出1: 信頼度0.95, IoU=0.85 → TP
検出2: 信頼度0.92, IoU=0.78 → TP
検出3: 信頼度0.88, IoU=0.42 → FP(IoU低い)
検出4: 信頼度0.85, IoU=0.90 → TP
検出5: 信頼度0.75, IoU=0.00 → FP(背景)
検出6: 信頼度0.65, IoU=0.72 → TP
正解の総数: 4個
─────────────────────────────────────────────────────
Step 2: 累積でTPとFPを計算し、PrecisionとRecallを求める
閾値 検出 TP累積 FP累積 Precision Recall
──────────────────────────────────────────────────────
0.95 TP 1 0 1/1=1.00 1/4=0.25
0.92 TP 2 0 2/2=1.00 2/4=0.50
0.88 FP 2 1 2/3=0.67 2/4=0.50
0.85 TP 3 1 3/4=0.75 3/4=0.75
0.75 FP 3 2 3/5=0.60 3/4=0.75
0.65 TP 4 2 4/6=0.67 4/4=1.00
─────────────────────────────────────────────────────
Step 3: Precision-Recall曲線をプロット
Precision
1.0 ●─●
│ ╲
0.75 │ ●
│ ╲
0.67 │ ●
0.60 │ ●
│ ╲
│ ●
└─────────────────→ Recall
0 0.25 0.50 0.75 1.0
7-2. Average Precision (AP) の計算
【APの計算方法(11点補間法)】
AP = (1/11) × Σ P_interp(r)
r ∈ {0.0, 0.1, 0.2, …, 1.0}
P_interp(r) = max{P(r’) : r’ ≥ r}
(Recall ≥ r となる最大のPrecision)
─────────────────────────────────────────────────────
【具体的な計算】
先ほどの例のPR曲線:
(Recall, Precision) = (0.25, 1.00), (0.50, 1.00), (0.50, 0.67),
(0.75, 0.75), (0.75, 0.60), (1.00, 0.67)
各Recall点での最大Precision(補間):
R=0.0: P_interp = max(1.00, 1.00, 0.67, 0.75, 0.60, 0.67) = 1.00
R=0.1: P_interp = 1.00(R≥0.1での最大)
R=0.2: P_interp = 1.00
R=0.3: P_interp = 1.00(R≥0.3: 0.50以降、最大は1.00)
R=0.4: P_interp = 1.00
R=0.5: P_interp = 1.00
R=0.6: P_interp = 0.75(R≥0.6: 0.75以降、最大は0.75)
R=0.7: P_interp = 0.75
R=0.8: P_interp = 0.67(R≥0.8: 1.00、最大は0.67)
R=0.9: P_interp = 0.67
R=1.0: P_interp = 0.67
AP = (1.00 + 1.00 + 1.00 + 1.00 + 1.00 + 1.00
+ 0.75 + 0.75 + 0.67 + 0.67 + 0.67) / 11
= 9.51 / 11
= 0.864
= 86.4%
7-3. mAP(mean Average Precision)
💡 mAPの計算
mAP = 各クラスのAPの平均
mAP = (1/C) × Σ AP_c
C: クラス数
AP_c: クラスcのAverage Precision
【mAPの計算例】
3クラス(cat、dog、person)のAPが以下の場合:
AP(cat) = 0.85
AP(dog) = 0.78
AP(person) = 0.92
mAP = (0.85 + 0.78 + 0.92) / 3
= 2.55 / 3
= 0.85
= 85%
─────────────────────────────────────────────────────
【異なるIoU閾値でのmAP】
■ mAP@0.5(PASCAL VOC方式)
・IoU閾値 = 0.5
・比較的緩い基準
・PASCAL VOCデータセットの標準
■ mAP@0.75
・IoU閾値 = 0.75
・より厳しい基準
・位置精度が重要な場合
■ mAP@[0.5:0.95](COCO方式)
・IoU閾値を0.5〜0.95まで0.05刻みで変化
・10個の閾値でAPを計算して平均
・最も厳しい基準
・COCOデータセットの標準
mAP = (AP@0.5 + AP@0.55 + … + AP@0.95) / 10
─────────────────────────────────────────────────────
【サイズ別のAP(COCO)】
AP_S(Small): 小物体のAP(面積 < 32²ピクセル)
AP_M(Medium): 中物体のAP(32² < 面積 < 96²)
AP_L(Large): 大物体のAP(面積 > 96²)
→ モデルの弱点を特定するのに有用
💻 8. mAPの実装
Pythonでmapを計算するコードを実装してみましょう。各関数の役割を理解することで、評価指標への理解が深まります。
8-1. IoU計算関数
※ コードが横に長い場合は横スクロールできます
import numpy as np
def compute_iou(box1, box2):
“””
2つのBounding BoxのIoUを計算
Args:
box1: [x1, y1, x2, y2] 形式のBox
box2: [x1, y1, x2, y2] 形式のBox
Returns:
iou: IoU値(0〜1)
“””
# 交差部分の座標を計算
# 左上座標: 両方の左上のmax
x1_inter = max(box1[0], box2[0])
y1_inter = max(box1[1], box2[1])
# 右下座標: 両方の右下のmin
x2_inter = min(box1[2], box2[2])
y2_inter = min(box1[3], box2[3])
# 交差部分の面積を計算
# 幅または高さが負の場合は交差なし(0)
inter_width = max(0, x2_inter – x1_inter)
inter_height = max(0, y2_inter – y1_inter)
inter_area = inter_width * inter_height
# 各Boxの面積を計算
box1_area = (box1[2] – box1[0]) * (box1[3] – box1[1])
box2_area = (box2[2] – box2[0]) * (box2[3] – box2[1])
# 和集合の面積 = Box1 + Box2 – 交差部分
union_area = box1_area + box2_area – inter_area
# IoUを計算(ゼロ除算を防ぐ)
iou = inter_area / union_area if union_area > 0 else 0
return iou
# テスト
box1 = [50, 30, 200, 150] # 正解Box
box2 = [55, 35, 195, 145] # 予測Box(少しずれている)
iou = compute_iou(box1, box2)
print(f”IoU: {iou:.4f}”)
実行結果:
IoU: 0.8571
8-2. Average Precision計算関数
def compute_ap(recalls, precisions):
“””
Precision-Recall曲線からAverage Precision (AP)を計算
11点補間法を使用
Args:
recalls: Recall値のリスト(昇順)
precisions: 対応するPrecision値のリスト
Returns:
ap: Average Precision(0〜1)
“””
# NumPy配列に変換
recalls = np.array(recalls)
precisions = np.array(precisions)
# 11点補間: Recall = 0.0, 0.1, 0.2, …, 1.0
recall_levels = np.linspace(0, 1, 11)
# 各Recall点での補間Precisionを計算
interpolated_precisions = []
for r in recall_levels:
# Recall ≥ r となる最大のPrecisionを取得
# そのようなRecallがない場合は0
mask = recalls >= r
if np.any(mask):
p_interp = np.max(precisions[mask])
else:
p_interp = 0
interpolated_precisions.append(p_interp)
# 平均を計算
ap = np.mean(interpolated_precisions)
return ap
# テスト
recalls = [0.25, 0.50, 0.50, 0.75, 0.75, 1.00]
precisions = [1.00, 1.00, 0.67, 0.75, 0.60, 0.67]
ap = compute_ap(recalls, precisions)
print(f”AP: {ap:.4f}”)
実行結果:
AP: 0.8636
8-3. mAP計算の完全な実装
def evaluate_detection(predictions, ground_truths, iou_threshold=0.5, num_classes=None):
“””
物体検出の評価(mAP計算)
Args:
predictions: 予測結果のリスト
[{‘box’: [x1,y1,x2,y2], ‘score’: 信頼度, ‘class’: クラスID}, …]
ground_truths: 正解のリスト
[{‘box’: [x1,y1,x2,y2], ‘class’: クラスID}, …]
iou_threshold: IoU閾値(デフォルト0.5)
num_classes: クラス数(Noneの場合は自動検出)
Returns:
mAP: mean Average Precision
class_aps: 各クラスのAP辞書
“””
# クラスを取得
if num_classes is None:
classes = set([gt[‘class’] for gt in ground_truths])
else:
classes = range(num_classes)
class_aps = {}
for cls in classes:
# このクラスの予測と正解を抽出
cls_preds = [p for p in predictions if p[‘class’] == cls]
cls_gts = [gt for gt in ground_truths if gt[‘class’] == cls]
# 正解が0の場合はスキップ
if len(cls_gts) == 0:
continue
# 予測を信頼度でソート(降順)
cls_preds = sorted(cls_preds, key=lambda x: x[‘score’], reverse=True)
# 各正解が検出されたかを追跡
detected = [False] * len(cls_gts)
# TP/FPを記録
tp_list = []
fp_list = []
for pred in cls_preds:
# 最もIoUが高い正解を探す
best_iou = 0
best_gt_idx = -1
for gt_idx, gt in enumerate(cls_gts):
# すでに検出済みの正解はスキップ
if detected[gt_idx]:
continue
iou = compute_iou(pred[‘box’], gt[‘box’])
if iou > best_iou:
best_iou = iou
best_gt_idx = gt_idx
# IoU閾値を超えているか判定
if best_iou >= iou_threshold and best_gt_idx != -1:
# True Positive
detected[best_gt_idx] = True
tp_list.append(1)
fp_list.append(0)
else:
# False Positive
tp_list.append(0)
fp_list.append(1)
# 累積和を計算
tp_cumsum = np.cumsum(tp_list)
fp_cumsum = np.cumsum(fp_list)
# PrecisionとRecallを計算
num_gts = len(cls_gts)
recalls = tp_cumsum / num_gts
precisions = tp_cumsum / (tp_cumsum + fp_cumsum)
# APを計算
ap = compute_ap(recalls, precisions)
class_aps[cls] = ap
print(f”Class {cls}: AP = {ap:.4f} (GT: {num_gts}, Predictions: {len(cls_preds)})”)
# mAPを計算
if len(class_aps) > 0:
mAP = np.mean(list(class_aps.values()))
else:
mAP = 0.0
return mAP, class_aps
# ========================================
# 使用例
# ========================================
# 予測結果
predictions = [
{‘box’: [50, 30, 200, 150], ‘score’: 0.95, ‘class’: 0}, # cat – TP
{‘box’: [300, 100, 450, 250], ‘score’: 0.92, ‘class’: 1}, # dog – TP
{‘box’: [55, 35, 205, 155], ‘score’: 0.88, ‘class’: 0}, # cat – FP(重複)
{‘box’: [100, 200, 150, 300], ‘score’: 0.75, ‘class’: 0}, # cat – FP(誤検出)
{‘box’: [310, 110, 460, 260], ‘score’: 0.70, ‘class’: 1}, # dog – FP(重複)
]
# 正解(Ground Truth)
ground_truths = [
{‘box’: [52, 32, 198, 148], ‘class’: 0}, # cat
{‘box’: [305, 105, 448, 248], ‘class’: 1}, # dog
]
# 評価を実行
print(“=” * 50)
print(“物体検出の評価結果”)
print(“=” * 50)
mAP, class_aps = evaluate_detection(predictions, ground_truths, iou_threshold=0.5)
print(“=” * 50)
print(f”mAP@0.5: {mAP:.4f}”)
実行結果:
==================================================
物体検出の評価結果
==================================================
Class 0: AP = 0.5000 (GT: 1, Predictions: 3)
Class 1: AP = 1.0000 (GT: 1, Predictions: 2)
==================================================
mAP@0.5: 0.7500
📝 練習問題
問題1:PrecisionとRecallの計算(基礎)
以下の検出結果について、PrecisionとRecallを計算してください。
正解の物体数: 10個
検出した物体数: 15個
そのうち正しい検出(TP): 8個
解答:
与えられた情報から:
・正解の物体数(Ground Truth)= 10個
・検出した物体数 = 15個
・正しい検出(TP)= 8個
TP、FP、FNを求める:
・TP(True Positive)= 8個
・FP(False Positive)= 検出数 – TP = 15 – 8 = 7個
・FN(False Negative)= GT – TP = 10 – 8 = 2個
Precisionの計算:
Precision = TP / (TP + FP)
= 8 / (8 + 7)
= 8 / 15
= 0.533
= 53.3%
Recallの計算:
Recall = TP / (TP + FN)
= 8 / (8 + 2)
= 8 / 10
= 0.800
= 80.0%
解釈:
・Precision 53.3%:検出したもののうち、約半分が正しい(誤検出が多い)
・Recall 80.0%:正解10個のうち、8個(80%)を検出できた(見逃しは少ない)
問題2:APの計算(中級)
以下のPrecision-Recall曲線から、11点補間法でAPを計算してください。
Recall: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
Precision: [1.0, 1.0, 0.9, 0.8, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3]
解答:
11点補間法:
Recall = 0.0, 0.1, 0.2, …, 1.0 の11点で評価
各Recall点での補間Precision(P_interp)を計算:
R=0.0: P_interp = max(全てのP) = 1.0
R=0.1: P_interp = max(R≥0.1でのP) = 1.0
R=0.2: P_interp = max(R≥0.2でのP) = 1.0
R=0.3: P_interp = max(R≥0.3でのP) = 0.9
R=0.4: P_interp = max(R≥0.4でのP) = 0.8
R=0.5: P_interp = max(R≥0.5でのP) = 0.8
R=0.6: P_interp = max(R≥0.6でのP) = 0.7
R=0.7: P_interp = max(R≥0.7でのP) = 0.6
R=0.8: P_interp = max(R≥0.8でのP) = 0.5
R=0.9: P_interp = max(R≥0.9でのP) = 0.4
R=1.0: P_interp = max(R≥1.0でのP) = 0.3
APの計算:
AP = (1.0 + 1.0 + 1.0 + 0.9 + 0.8 + 0.8 + 0.7 + 0.6 + 0.5 + 0.4 + 0.3) / 11
= 8.0 / 11
= 0.727
= 72.7%
問題3:mAPの計算と評価基準の比較(応用)
3クラス(cat、dog、person)のAPが以下の場合、mAP@0.5とmAP@0.75を計算し、結果を解釈してください。
IoU=0.5 IoU=0.75
cat 0.85 0.65
dog 0.78 0.58
person 0.92 0.78
解答:
mAP@0.5の計算:
mAP@0.5 = (AP_cat + AP_dog + AP_person) / 3
= (0.85 + 0.78 + 0.92) / 3
= 2.55 / 3
= 0.85 = 85.0%
mAP@0.75の計算:
mAP@0.75 = (AP_cat + AP_dog + AP_person) / 3
= (0.65 + 0.58 + 0.78) / 3
= 2.01 / 3
= 0.67 = 67.0%
結果の解釈:
・mAP@0.5(85%)とmAP@0.75(67%)の差は18%
・IoU閾値が厳しくなると、mAPが大幅に下がる
・このモデルは「物体を見つける」能力は高いが、「正確な位置を特定する」能力には改善の余地がある
・personのAPはIoU=0.75でも78%と高い → 位置精度が高い
・dogのAPはIoU=0.75で58%と低い → 位置精度に改善の余地がある
問題4:Focal Lossの効果(応用)
Cross Entropy LossとFocal Loss(γ=2)で、以下の予測確率に対する損失を計算し、効果を比較してください。
予測確率 p = 0.95(簡単な例)
予測確率 p = 0.2(難しい例)
解答:
簡単な例(p = 0.95):
Cross Entropy: CE = -log(0.95) = 0.0513
Focal Loss: FL = -(1-0.95)^2 × log(0.95)
= -(0.05)^2 × 0.0513
= -0.0025 × 0.0513
= 0.00013
削減率: (0.0513 – 0.00013) / 0.0513 = 99.7%
難しい例(p = 0.2):
Cross Entropy: CE = -log(0.2) = 1.609
Focal Loss: FL = -(1-0.2)^2 × log(0.2)
= -(0.8)^2 × 1.609
= -0.64 × 1.609
= 1.030
削減率: (1.609 – 1.030) / 1.609 = 36.0%
効果の比較:
| 予測確率 |
CE Loss |
Focal Loss |
削減率 |
| 0.95(簡単) |
0.0513 |
0.00013 |
99.7% |
| 0.2(難しい) |
1.609 |
1.030 |
36.0% |
結論:
・簡単な例の損失を99.7%削減(ほぼ無視)
・難しい例の損失は36%削減(まだ大きい)
→ モデルは難しい例に集中して学習できる!
📝 STEP 12 のまとめ
✅ このステップで学んだこと
1. 1段階検出モデル
・SSD: マルチスケール検出、Default Box
・RetinaNet: Focal Lossでクラス不均衡を解決
・EfficientDet: BiFPN、Compound Scaling
2. 評価指標
・TP、FP、FN の分類
・Precision = TP/(TP+FP)
・Recall = TP/(TP+FN)
・IoUによる正解/不正解の判定
3. AP と mAP
・Precision-Recall曲線の作成
・11点補間法によるAP計算
・mAP = 各クラスのAPの平均
・mAP@0.5 vs mAP@[0.5:0.95]
💡 重要ポイント
物体検出の評価では、mAPが最も広く使われる指標です。PASCAL VOC(mAP@0.5)とCOCO(mAP@[0.5:0.95])では評価基準が異なるため、論文やモデル比較の際は注意が必要です。
Focal Lossは、クラス不均衡問題を解決する画期的な手法で、RetinaNetによって1段階検出が2段階検出を超える精度を達成しました。
次のSTEP 13では、「セマンティックセグメンテーション」を学びます。Bounding Boxではなく、ピクセル単位で物体を認識する技術です。