📋 このステップで学ぶこと
- インスタンスセグメンテーションとは(セマンティックとの違い)
- Mask R-CNN(Faster R-CNNの拡張、マスク分岐)
- RoIAlign(RoI Poolingの問題点と解決策)
- 損失関数(分類、Box回帰、マスクの3つ)
- YOLACT(プロトタイプマスク、リアルタイム処理)
- 評価指標(Mask IoU、Mask AP、Box AP vs Mask AP)
- 実装:Mask R-CNNでのインスタンスセグメンテーション
🎯 1. インスタンスセグメンテーションとは
インスタンスセグメンテーションは、画像内の各物体を個別に識別し、ピクセル単位のマスクを生成するタスクです。STEP 13で学んだセマンティックセグメンテーションが「クラスごと」に分類するのに対し、インスタンスセグメンテーションは「物体ごと」に分離します。
1-1. セマンティック vs インスタンスセグメンテーション
【2つのタスクの根本的な違い】
■ セマンティックセグメンテーション(STEP 13で学習)
質問: 「各ピクセルは何クラス?」
出力: クラスマップ(H×W)
各ピクセルにクラスID
例: 2匹の猫がいる画像
元画像 セグメンテーション結果
┌─────────────┐ ┌─────────────┐
│ 🐱 🐱 │ → │ 猫猫 猫猫 │
│ (猫A)(猫B) │ │ ← 区別なし →│
│ 背景 │ │ 背景 │
└─────────────┘ └─────────────┘
特徴:
・2匹の猫は同じ「猫」クラスとして扱う
・個体の区別ができない
・「猫が何匹いるか」は分からない
─────────────────────────────────────────────────────
■ インスタンスセグメンテーション
質問: 「各物体はどこにある?形状は?」
出力: 物体ごとのマスク(複数)
各物体に固有のID
例: 2匹の猫がいる画像
元画像 セグメンテーション結果
┌─────────────┐ ┌─────────────┐
│ 🐱 🐱 │ → │ 猫① 猫② │
│ (猫A)(猫B) │ │ ← 個別識別 →│
│ 背景 │ │ (なし) │
└─────────────┘ └─────────────┘
特徴:
・2匹の猫を「猫①」「猫②」と区別
・個体ごとに分離したマスク
・「猫が2匹いる」ことが分かる
・背景はラベル付けしない
1-2. インスタンスセグメンテーションの出力形式
💡 インスタンスセグメンテーションの出力
物体検出(Bounding Box)+ セマンティックセグメンテーション(マスク)の組み合わせ
【出力の詳細】
検出された物体ごとに以下の情報を出力:
物体1:
├─ クラス: cat(猫)
├─ 信頼度: 0.95(95%の確信)
├─ Bounding Box: (x1=50, y1=30, x2=200, y2=180)
│ → 物体を囲む矩形
└─ マスク: (H×W)のバイナリマスク
→ 物体の正確な形状(ピクセル単位)
→ 1=物体、0=背景
物体2:
├─ クラス: cat(猫)
├─ 信頼度: 0.88
├─ Bounding Box: (300, 100, 450, 250)
└─ マスク: (H×W)のバイナリマスク
物体3:
├─ クラス: dog(犬)
├─ 信頼度: 0.92
├─ Bounding Box: (500, 200, 700, 400)
└─ マスク: (H×W)のバイナリマスク
─────────────────────────────────────────────────────
【マスクの例】
Bounding Box内のマスク(28×28で予測 → 元サイズにリサイズ):
0 0 0 0 0 0 0 0
0 0 1 1 1 1 0 0
0 1 1 1 1 1 1 0
0 1 1 1 1 1 1 0 1 = 物体のピクセル
0 1 1 1 1 1 1 0 0 = 背景のピクセル
0 1 1 1 1 1 1 0
0 0 1 1 1 1 0 0
0 0 0 0 0 0 0 0
→ 物体の輪郭を正確に表現
1-3. いつインスタンスセグメンテーションが必要か
| インスタンスが必要なケース |
セマンティックで十分なケース |
| ✅ 物体の個数をカウントしたい |
領域の面積だけ知りたい |
| ✅ 個別の物体を追跡したい |
物体の種類だけ分かればいい |
| ✅ 重なっている物体を分離したい |
個数に興味がない |
| ✅ 特定の物体だけ切り抜きたい |
背景と前景の分離だけでいい |
1-4. 応用分野
| 分野 |
用途 |
なぜインスタンスが必要か |
具体例 |
| ロボティクス |
物体の把持、操作 |
個別の物体を掴むため |
工場の部品ピックアップ |
| 自動運転 |
車両・歩行者認識 |
各車両を個別に追跡 |
Tesla、Waymo |
| 画像編集 |
個別物体の切り抜き |
特定の人物だけ抽出 |
Photoshop、スマホアプリ |
| 医療画像 |
細胞・病変検出 |
細胞数をカウント |
病理診断 |
| 農業 |
果実のカウント |
収穫量を予測 |
りんご、ぶどうの数 |
| 小売業 |
在庫管理 |
商品数を自動カウント |
棚の商品管理 |
🎭 2. Mask R-CNN
Mask R-CNN(2017年)は、STEP 10で学んだFaster R-CNNにマスク予測ブランチを追加したモデルです。物体検出とセグメンテーションを同時に行う、インスタンスセグメンテーションの代表的なモデルです。
2-1. Faster R-CNNからMask R-CNNへの進化
【Faster R-CNN(復習)】
入力画像
↓
┌─────────────────────────────────────────────────────┐
│ バックボーン(ResNet等) │
│ → 特徴マップを抽出 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ RPN(Region Proposal Network) │
│ → 物体がありそうな領域(候補領域)を提案 │
│ → 約2000個の候補を生成 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ RoI Pooling │
│ → 候補領域から固定サイズの特徴を抽出 │
│ → 7×7×Cの特徴マップに変換 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ ヘッド(2つの分岐) │
│ │
│ ┌────────────┐ ┌────────────┐ │
│ │ 分類ヘッド │ │ Box回帰 │ │
│ │ (FC層) │ │ (FC層) │ │
│ │ ↓ │ │ ↓ │ │
│ │ クラス確率 │ │ Box調整量 │ │
│ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────┘
↓
出力: クラス + Bounding Box
─────────────────────────────────────────────────────
【Mask R-CNN(新規)】
入力画像
↓
┌─────────────────────────────────────────────────────┐
│ バックボーン(ResNet-50/101 + FPN) │
│ → マルチスケールの特徴マップを抽出 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ RPN(Region Proposal Network) │
│ → 候補領域を提案 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ RoIAlign(★改良点1) │
│ → 量子化なしで正確な特徴抽出 │
│ → 14×14×Cの特徴マップに変換 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ ヘッド(3つの分岐) │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ 分類ヘッド │ │ Box回帰 │ │ マスクヘッド│ │
│ │ (FC層) │ │ (FC層) │ │ (Conv層) │ │
│ │ ↓ │ │ ↓ │ │ ↓ │ │
│ │ クラス確率 │ │ Box調整量 │ │ 28×28マスク│ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ ★改良点2 │
└─────────────────────────────────────────────────────┘
↓
出力: クラス + Bounding Box + マスク
─────────────────────────────────────────────────────
【Mask R-CNNの2つの改良点】
1. RoIAlign(RoI Poolingの改良)
→ ピクセル単位の精度を向上
2. マスク予測ブランチの追加
→ 物体の形状を予測
2-2. RoI Pooling vs RoIAlign
⚠️ RoI Poolingの問題点
RoI Poolingは座標を整数に丸める(量子化)ため、位置情報が不正確になります。物体検出では問題になりにくいですが、ピクセル単位のマスク予測では致命的です。
【RoI Poolingの処理と問題】
■ 例: 元画像での物体位置
元画像(800×600)での座標:
Bounding Box = (100, 80, 200, 180)
幅 = 100、高さ = 100
特徴マップのサイズ: 元画像の1/16(stride=16)
800×600 → 50×37.5
■ RoI Poolingの処理
Step 1: 特徴マップ上の座標に変換
(100/16, 80/16, 200/16, 180/16)
= (6.25, 5.0, 12.5, 11.25)
Step 2: 整数に丸める(量子化)★問題の原因
(6, 5, 12, 11)
→ 6.25 → 6(0.25の誤差)
→ 12.5 → 12(0.5の誤差)
Step 3: 固定サイズ(7×7)に分割
領域サイズ: 6×6
セルサイズ: 6/7 = 0.857 → 0または1
→ さらに量子化誤差
Step 4: 各セルでMax Pooling
■ 問題点
累積誤差:
特徴マップで0.5ピクセルの誤差
→ 元画像で 0.5 × 16 = 8ピクセルの誤差
影響:
・Bounding Box → 影響小(大まかな位置で十分)
・マスク → 影響大(境界が8ピクセルずれる)
[正しいマスク] [ずれたマスク]
┌────────┐ ┌────────┐
│ ████ │ → │ ████ │ 境界がずれる
│ ████ │ │ ████ │
└────────┘ └────────┘
💡 RoIAlignの解決策
「量子化(整数への丸め)をしない」
代わりに「双線形補間」で正確な値を計算
【RoIAlignの処理】
■ 同じ例: 元画像での物体位置
元画像での座標: (100, 80, 200, 180)
特徴マップ上の座標: (6.25, 5.0, 12.5, 11.25)
■ RoIAlignの処理
Step 1: 座標を小数のまま保持
(6.25, 5.0, 12.5, 11.25) ← 量子化しない
Step 2: 固定サイズ(14×14)に分割
各セルのサイズを計算
幅: (12.5 – 6.25) / 14 = 0.446
高さ: (11.25 – 5.0) / 14 = 0.446
Step 3: 各セル内でサンプリング点を設定
各セル内に2×2=4点を設定
例: セル(0,0)のサンプリング点
・(6.25 + 0.111, 5.0 + 0.111) = (6.361, 5.111)
・(6.25 + 0.334, 5.0 + 0.111) = (6.584, 5.111)
・(6.25 + 0.111, 5.0 + 0.334) = (6.361, 5.334)
・(6.25 + 0.334, 5.0 + 0.334) = (6.584, 5.334)
Step 4: 双線形補間で各点の値を計算
小数座標の値を周囲4点から補間
Step 5: サンプリング点の値を平均
セル(0,0)の値 = 4点の平均
■ 双線形補間の詳細
座標 (6.361, 5.111) の値を求める:
周囲の4つの格子点:
・(6, 5) の値 = v00
・(7, 5) の値 = v10
・(6, 6) の値 = v01
・(7, 6) の値 = v11
重み計算:
dx = 6.361 – 6 = 0.361
dy = 5.111 – 5 = 0.111
補間:
値 = v00×(1-dx)×(1-dy) + v10×dx×(1-dy)
+ v01×(1-dx)×dy + v11×dx×dy
= v00×0.639×0.889 + v10×0.361×0.889
+ v01×0.639×0.111 + v11×0.361×0.111
■ 結果
量子化誤差: なし
位置精度: 元画像レベルで正確
マスク品質: 境界が鮮明
【RoI Pooling vs RoIAlign の比較】
┌─────────────┬──────────────────┬──────────────────┐
│ 項目 │ RoI Pooling │ RoIAlign │
├─────────────┼──────────────────┼──────────────────┤
│ 量子化 │ あり(整数に丸め)│ なし(小数保持) │
│ 補間 │ なし │ 双線形補間 │
│ 位置精度 │ 低い │ 高い │
│ 出力サイズ │ 7×7(通常) │ 14×14(通常) │
│ 用途 │ 物体検出 │ マスク予測 │
│ マスクAP │ – │ 約3%向上 │
└─────────────┴──────────────────┴──────────────────┘
→ インスタンスセグメンテーションにはRoIAlignが必須!
2-3. マスク予測ブランチ
【マスク予測ブランチの構造】
RoIAlignの出力: 14×14×256
│
↓
┌─────────────────────────────────────────────────────┐
│ マスクヘッド(全て畳み込み層) │
│ │
│ 3×3 Conv (256) → ReLU │
│ ↓ │
│ 3×3 Conv (256) → ReLU │
│ ↓ │
│ 3×3 Conv (256) → ReLU │
│ ↓ │
│ 3×3 Conv (256) → ReLU │
│ ↓ │
│ 2×2 Deconv (256) ← 転置畳み込み(アップサンプリング)│
│ ↓ 14×14 → 28×28 │
│ 1×1 Conv (num_classes) ← クラス数分のマスク │
│ ↓ │
│ Sigmoid │
└─────────────────────────────────────────────────────┘
│
↓
出力: 28×28×num_classes
各クラスに対して28×28のバイナリマスク
─────────────────────────────────────────────────────
【なぜクラスごとにマスクを予測するか】
従来の方法(クラスに依存しないマスク):
出力: 28×28×1
→ 前景/背景の2クラス分類
→ 問題: クラス間の競合
Mask R-CNNの方法(クラスごとのマスク):
出力: 28×28×num_classes
→ 各クラスに独立したマスク
例: 80クラスの場合
出力: 28×28×80
推論時:
1. 分類ヘッドが「猫」と予測
2. 猫のマスク(28×28×1)だけを使用
3. 他のクラスのマスクは無視
利点:
・クラス間の競合がない
・各クラスの特徴的な形状を学習可能
・精度が向上
2-4. Mask R-CNNの損失関数
【Mask R-CNNの総損失】
L_total = L_cls + L_box + L_mask
─────────────────────────────────────────────────────
■ 1. 分類損失(L_cls)
Cross Entropy Loss
L_cls = -log(p_k)
p_k: 正解クラスkの予測確率
例:
正解: 猫
予測: [背景:0.02, 猫:0.90, 犬:0.08]
L_cls = -log(0.90) = 0.105
─────────────────────────────────────────────────────
■ 2. Bounding Box損失(L_box)
Smooth L1 Loss(Huber Loss)
L_box = Σ smooth_L1(t_i – v_i)
t_i: 予測されたBox調整量 (tx, ty, tw, th)
v_i: 正解のBox調整量
smooth_L1(x) = {
0.5 × x² if |x| < 1
|x| - 0.5 otherwise
}
→ 外れ値に強い損失関数
─────────────────────────────────────────────────────
■ 3. マスク損失(L_mask)★新規
Binary Cross Entropy(ピクセルごと)
L_mask = -(1/m²) × Σ[y×log(p) + (1-y)×log(1-p)]
m: マスクサイズ(28)
y: 正解マスク(0 or 1)
p: 予測確率(Sigmoid出力)
重要な点:
・予測されたクラスのマスクにのみ適用
・「猫」と予測 → 「猫」のマスク損失のみ計算
・他のクラスのマスクは損失に含めない
理由:
各クラスが独立して学習するため
クラス間の競合を避ける
─────────────────────────────────────────────────────
【損失の重み】
実際の実装:
L_total = L_cls + L_box + L_mask
通常は等しい重みだが、調整可能:
L_total = λ_cls × L_cls + λ_box × L_box + λ_mask × L_mask
2-5. Mask R-CNNの全体フロー
【Mask R-CNNの完全な処理フロー】
入力画像(例: 800×600×3)
│
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Stage 1: バックボーン(ResNet-50 + FPN)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ResNet-50で特徴抽出
│
├─ C2 (200×150×256)
├─ C3 (100×75×512)
├─ C4 (50×37×1024)
└─ C5 (25×18×2048)
│
↓
FPN(Feature Pyramid Network)で融合
│
├─ P2 (200×150×256) ← 小さい物体用
├─ P3 (100×75×256)
├─ P4 (50×37×256) ← 中程度の物体用
├─ P5 (25×18×256)
└─ P6 (12×9×256) ← 大きい物体用
│
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Stage 2: RPN(Region Proposal Network)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
各FPNレベルでAnchor Boxを生成
│
├─ P2: 小さいAnchor
├─ P3:
├─ P4:
├─ P5:
└─ P6: 大きいAnchor
│
↓
物体らしさを予測 + Box回帰
│
↓
約2000個の候補領域(Region Proposals)
│
↓
NMS(Non-Maximum Suppression)で絞り込み
│
↓
約1000個の候補領域
│
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Stage 3: RoIAlign
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
各候補領域から適切なFPNレベルを選択
(大きい領域 → P5、小さい領域 → P2)
│
↓
RoIAlignで固定サイズの特徴抽出
│
↓
14×14×256 の特徴マップ(各候補領域)
│
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Stage 4: 3つのヘッド(並列処理)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┌──────────────┬──────────────┬──────────────┐
│ 分類ヘッド │ Box回帰ヘッド│ マスクヘッド │
│ │ │ │
│ 14×14×256 │ 14×14×256 │ 14×14×256 │
│ ↓ │ ↓ │ ↓ │
│ Flatten │ Flatten │ Conv×4 │
│ ↓ │ ↓ │ ↓ │
│ FC(1024) │ FC(1024) │ Deconv │
│ ↓ │ ↓ │ ↓ │
│ FC(num_cls) │ FC(4) │ 1×1 Conv │
│ ↓ │ ↓ │ ↓ │
│ Softmax │ (回帰値) │ Sigmoid │
│ ↓ │ ↓ │ ↓ │
│ クラス確率 │ Box調整量 │ 28×28×C │
└──────────────┴──────────────┴──────────────┘
│
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Stage 5: 後処理
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 信頼度でフィルタリング(閾値: 0.7など)
2. Box回帰を適用してBounding Boxを調整
3. クラスごとにNMSで重複除去
4. 予測クラスのマスクを選択
5. マスクを元の画像サイズにリサイズ
│
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
出力
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
各検出物体について:
– クラス(例: “cat”)
– 信頼度(例: 0.95)
– Bounding Box(例: [100, 80, 200, 180])
– マスク(元画像サイズ、バイナリ)
⚡ 3. YOLACT(リアルタイムインスタンスセグメンテーション)
YOLACT(You Only Look At CoefficienTs, 2019年)は、リアルタイムで動作するインスタンスセグメンテーションモデルです。Mask R-CNNの約6倍の速度を実現しています。
3-1. Mask R-CNNの速度問題とYOLACTの解決策
【Mask R-CNNが遅い理由】
処理フロー:
1. RPN: 候補領域を提案(約2000個)
2. RoIAlign: 各候補領域から特徴抽出
3. マスクヘッド: 各候補領域でマスク予測
ボトルネック:
・候補領域ごとにマスクを予測
・候補が多いと処理が遅い
・逐次処理になりがち
速度: 約5 FPS(GPU)
→ リアルタイム(30 FPS)には程遠い
─────────────────────────────────────────────────────
【YOLACTの高速化アイデア】
核心:
「マスクを直接予測せず、プロトタイプの線形結合で表現」
処理フロー:
1. Protonet: 画像全体でk個のプロトタイプマスクを生成
→ 画像全体で1回だけ、並列処理
2. Prediction: 各物体でk個の係数を予測
→ すべての物体で並列処理
3. Assembly: 線形結合で最終マスクを生成
→ 行列演算で高速
速度: 約33 FPS(GPU)
→ リアルタイム処理が可能!
3-2. プロトタイプマスクとは
💡 プロトタイプの概念
「プロトタイプ」は、様々なマスクを表現するための基本パターン集。
複数のプロトタイプを組み合わせることで、任意の形状を表現できます。
【プロトタイプマスクの例(k=32個)】
各プロトタイプは138×138のマスク:
P1: 左側を強調 P2: 右側を強調
┌────────────┐ ┌────────────┐
│████▒▒▒▒ │ │ ▒▒▒▒████│
│████▒▒▒▒ │ │ ▒▒▒▒████│
│████▒▒▒▒ │ │ ▒▒▒▒████│
└────────────┘ └────────────┘
P3: 上側を強調 P4: 下側を強調
┌────────────┐ ┌────────────┐
│████████████│ │ │
│████████████│ │ │
│▒▒▒▒▒▒▒▒▒▒▒▒│ │████████████│
│ │ │████████████│
└────────────┘ └────────────┘
P5: 中央を強調 P6: 円形
┌────────────┐ ┌────────────┐
│ ▒▒▒▒ │ │ ▒▒▒▒▒▒ │
│ ████████ │ │ ▒▒████████▒▒│
│ ████████ │ │ ▒▒████████▒▒│
│ ▒▒▒▒ │ │ ▒▒▒▒▒▒ │
└────────────┘ └────────────┘
… P7〜P32 も様々なパターン
─────────────────────────────────────────────────────
【なぜプロトタイプで表現できるか】
数学的な直感:
任意の関数は基底関数の線形結合で表現可能
(フーリエ変換と同じ考え方)
プロトタイプ = 基底関数
係数 = 各基底関数の重み
→ k個のプロトタイプがあれば、
多様なマスク形状を表現可能
3-3. 線形結合によるマスク生成
【マスク生成の計算】
■ 入力
プロトタイプマスク P: (H×W×k) = (138×138×32)
P[:,:,0] = P1(左側強調)
P[:,:,1] = P2(右側強調)
…
P[:,:,31] = P32
物体iの係数 C_i: (k,) = (32,)
C_i = [c1, c2, c3, …, c32]
■ 計算
最終マスク M_i = σ(Σ c_j × P_j)
= σ(c1×P1 + c2×P2 + … + c32×P32)
σ: Sigmoid関数(0〜1に正規化)
■ 具体例: 左上にある猫のマスク
係数 C = [0.8, 0.1, 0.7, 0.2, 0.1, 0.0, …, 0.0]
計算:
M = σ(0.8×P1 + 0.1×P2 + 0.7×P3 + 0.2×P4 + …)
= σ(0.8×[左側] + 0.1×[右側] + 0.7×[上側] + …)
[左側強調] [上側強調]
████▒▒▒▒ + ████████████
████▒▒▒▒ ████████████ ← 左側×0.8
████▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒ ← 上側×0.7
↓ 線形結合
[結果: 左上を強調]
████████▒▒▒▒
████████▒▒▒▒
▒▒▒▒▒▒▒▒
→ 係数の組み合わせで様々な形状を表現!
■ 行列演算での実装
すべての物体を一度に計算:
M = σ(P × C^T)
P: (H×W, k) = (138×138, 32)
C: (N, k) = (物体数, 32)
→ M: (H×W, N) = (138×138, 物体数)
→ 並列計算で高速!
3-4. YOLACTのアーキテクチャ
【YOLACTの構造】
入力画像(550×550)
│
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
バックボーン(ResNet-101 + FPN)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│
├─ P3 (69×69)
├─ P4 (35×35)
├─ P5 (18×18)
├─ P6 (9×9)
└─ P7 (5×5)
│
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
並列処理(2つのブランチ)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│
┌────┴────┐
│ │
↓ ↓
┌─────────┐ ┌─────────────────────────────────┐
│ Protonet │ │ Prediction Head │
│ │ │ │
│ P3を使用 │ │ 各FPNレベルで: │
│ ↓ │ │ ・クラス確率 (num_classes) │
│ Conv×3 │ │ ・Bounding Box (4) │
│ ↓ │ │ ・マスク係数 (k=32) │
│ 3×3 Conv │ │ │
│ (k=32) │ │ YOLOv3ライクな検出 │
│ ↓ │ │ │
│ Upsample │ │ │
│ ↓ │ │ │
│ ReLU │ │ │
│ ↓ │ │ │
│138×138×32│ │ │
└─────────┘ └─────────────────────────────────┘
│ │
│ ↓
│ 各物体の検出結果:
│ – クラス
│ – Bounding Box
│ – 係数 C (32次元)
│ │
━━━━│━━━━━━━━━│━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Assembly(組み立て)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│ │
└────┬────┘
│
↓
M = σ(P × C^T)
│
↓
Bounding Box内でクロップ
│
↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
出力
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
各物体:
– クラス
– Bounding Box
– マスク(元画像サイズ)
3-5. Mask R-CNN vs YOLACT
| 項目 |
Mask R-CNN |
YOLACT |
| 処理方式 |
2段階(候補→マスク) |
1段階(並列処理) |
| マスク生成 |
各物体で直接予測 |
プロトタイプの線形結合 |
| 速度(GPU) |
約5 FPS |
約33 FPS |
| 精度(COCO mAP) |
37.1% |
29.8% |
| マスク解像度 |
28×28(物体ごと) |
138×138(画像全体) |
| 得意な場面 |
高精度、小さい物体 |
リアルタイム、動画 |
🎯 使い分けのガイドライン
Mask R-CNNを選ぶ場合:
・精度が最優先
・オフライン処理(バッチ処理)
・小さい物体の検出が重要
・境界の精度が必要
YOLACTを選ぶ場合:
・リアルタイム処理が必要(30 FPS以上)
・動画処理
・エッジデバイスでの推論
・やや精度を犠牲にしても速度が欲しい
YOLACT++:
YOLACTの改良版、精度と速度のバランスがより良い(mAP 34.1%、27 FPS)
📊 4. 評価指標
4-1. Mask IoU
【Mask IoU vs Box IoU】
■ Box IoU(物体検出で使用)
Bounding Boxの重なりで計算
Box_IoU = (Box_予測 ∩ Box_正解) / (Box_予測 ∪ Box_正解)
例:
┌─────────┐
│ 予測Box │
│ ┌────┼───┐
│ │重なり│ │
└────┼────┘ │
│ 正解Box │
└────────┘
→ 矩形の重なり面積で計算
─────────────────────────────────────────────────────
■ Mask IoU(インスタンスセグメンテーションで使用)
マスクのピクセル単位で計算
Mask_IoU = (Mask_予測 ∩ Mask_正解) / (Mask_予測 ∪ Mask_正解)
例:
予測マスク 正解マスク 重なり
░░██████░░ ░░░░████████ ░░░░████░░
░░████████░ ░░░░██████░░ → ░░░░████░░
░░██████░░ ░░░░████░░░░ ░░░░████░░
重なりピクセル数 / 和集合ピクセル数
─────────────────────────────────────────────────────
【計算例】
予測マスク: 物体のピクセル数 = 200個
正解マスク: 物体のピクセル数 = 250個
重なるピクセル: 160個
Mask_IoU = 160 / (200 + 250 – 160)
= 160 / 290
= 0.552
= 55.2%
─────────────────────────────────────────────────────
【Mask IoU vs Box IoU の違い】
同じ物体でも:
Box IoU = 0.75(矩形が大まかに合っている)
Mask IoU = 0.55(形状が正確には合っていない)
→ Mask IoUの方が厳しい評価
→ 境界の精度が反映される
4-2. Mask AP(Average Precision)
【Mask APの計算】
■ 基本的な流れ(STEP 12の復習 + マスク版)
1. 各検出を信頼度順にソート
2. 各検出について:
・正解マスクとのMask IoUを計算
・IoU ≥ 閾値 → TP(True Positive)
・IoU < 閾値 → FP(False Positive)
3. Precision-Recall曲線を計算
4. 曲線の下面積(AUC)= AP
─────────────────────────────────────────────────────
■ COCO評価指標
AP(Average Precision):
IoU閾値を0.50〜0.95まで0.05刻みで計算し、平均
AP = (AP@0.50 + AP@0.55 + ... + AP@0.95) / 10
個別の閾値:
AP50: IoU 0.50でのAP(緩い基準)
AP75: IoU 0.75でのAP(厳しい基準)
サイズ別:
AP_S: 小さい物体(面積 < 32²ピクセル)
AP_M: 中程度の物体(32² < 面積 < 96²)
AP_L: 大きい物体(面積 > 96²)
─────────────────────────────────────────────────────
■ Mask R-CNNの性能(COCO val2017)
Mask AP: 34.2%
Mask AP50: 55.5% ← 緩い基準では高い
Mask AP75: 36.2% ← 厳しい基準では低下
Box AP: 37.1% ← Mask APより高い
Box AP50: 58.6%
Box AP75: 40.1%
観察:
・Mask AP < Box AP(マスクの方が難しい)
・AP50 >> AP75(境界の精度が課題)
・AP_S < AP_M < AP_L(小さい物体は難しい)
4-3. 評価指標のまとめ
| 指標 |
定義 |
特徴・用途 |
| Mask IoU |
マスクのピクセル重なり |
ピクセル単位の精度、Box IoUより厳しい |
| Mask AP |
複数IoU閾値の平均 |
総合的な性能評価、COCO標準 |
| Mask AP50 |
IoU 0.5でのAP |
緩い基準、検出能力の評価 |
| Mask AP75 |
IoU 0.75でのAP |
厳しい基準、境界精度の評価 |
| AP_S/M/L |
サイズ別のAP |
物体サイズごとの性能分析 |
💻 5. 実装:Mask R-CNNでのインスタンスセグメンテーション
5-1. モデルのロードと画像の前処理
※ コードが横に長い場合は横スクロールできます
import torch
import torchvision
from torchvision.models.detection import maskrcnn_resnet50_fpn
from torchvision.transforms import functional as F
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# ===================================================
# 1. 事前学習済みモデルのロード
# ===================================================
# Mask R-CNN (ResNet-50 + FPN バックボーン)
# pretrained=True: COCOデータセットで事前学習済みの重みを使用
# weights=”DEFAULT”でも可(PyTorch 2.0以降)
model = maskrcnn_resnet50_fpn(pretrained=True)
# 評価モードに設定
# Dropout、BatchNormの挙動が訓練時と異なる
model.eval()
# GPUが利用可能ならGPUに転送
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
model = model.to(device)
print(f”デバイス: {device}”)
print(“Mask R-CNNモデルをロードしました”)
実行結果:
デバイス: cuda
Mask R-CNNモデルをロードしました
5-2. 画像の読み込みと推論
# ===================================================
# 2. 画像の前処理
# ===================================================
def preprocess_image(image_path):
“””
画像を読み込んでモデル入力形式に変換
Args:
image_path: 画像ファイルのパス
Returns:
image_tensor: モデル入力用のテンソル [C, H, W]
original_image: 表示用の元画像 (PIL Image)
“””
# PIL Imageとして読み込み
image = Image.open(image_path).convert(‘RGB’)
# PIL Image → Tensor [C, H, W]、値は0〜1に正規化
# F.to_tensor()は自動的に[0, 255] → [0, 1]に変換
image_tensor = F.to_tensor(image)
return image_tensor, image
# 画像を読み込み
image_tensor, original_image = preprocess_image(‘sample.jpg’)
# バッチ次元を追加してGPUに転送
# model()はリスト形式の入力を期待
image_tensor = image_tensor.to(device)
print(f”画像サイズ: {original_image.size}”) # (W, H)
print(f”テンソル形状: {image_tensor.shape}”) # [C, H, W]
# ===================================================
# 3. 推論
# ===================================================
# 勾配計算を無効化(メモリ節約、高速化)
with torch.no_grad():
# リスト形式で入力、結果もリストで返される
# prediction[0]に最初の画像の結果
predictions = model([image_tensor])
prediction = predictions[0]
# 結果の確認
print(f”\n検出された物体数: {len(prediction[‘labels’])}”)
print(f”出力キー: {prediction.keys()}”)
実行結果:
画像サイズ: (640, 480)
テンソル形状: torch.Size([3, 480, 640])
検出された物体数: 15
出力キー: dict_keys([‘boxes’, ‘labels’, ‘scores’, ‘masks’])
5-3. 結果の処理とフィルタリング
# ===================================================
# 4. COCOクラス名の定義
# ===================================================
# COCOデータセットの91クラス(0は背景)
# Mask R-CNNは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’
]
# ===================================================
# 5. 信頼度でフィルタリング
# ===================================================
# 信頼度閾値(0.7 = 70%以上の確信がある検出のみ採用)
threshold = 0.7
# 閾値以上の検出のインデックスを取得
high_conf_indices = prediction[‘scores’] > threshold
# フィルタリング
filtered_boxes = prediction[‘boxes’][high_conf_indices]
filtered_labels = prediction[‘labels’][high_conf_indices]
filtered_scores = prediction[‘scores’][high_conf_indices]
filtered_masks = prediction[‘masks’][high_conf_indices]
print(f”信頼度 > {threshold} の物体数: {len(filtered_labels)}”)
# 各物体の情報を表示
print(“\n検出結果:”)
print(“-” * 50)
for i, (box, label, score) in enumerate(zip(
filtered_boxes, filtered_labels, filtered_scores
)):
class_name = COCO_CLASSES[label]
box_np = box.cpu().numpy()
print(f”物体 {i+1}:”)
print(f” クラス: {class_name}”)
print(f” 信頼度: {score:.3f}”)
print(f” Box: [{box_np[0]:.1f}, {box_np[1]:.1f}, {box_np[2]:.1f}, {box_np[3]:.1f}]”)
実行結果:
信頼度 > 0.7 の物体数: 5
検出結果:
————————————————–
物体 1:
クラス: person
信頼度: 0.998
Box: [120.5, 45.2, 380.3, 475.1]
物体 2:
クラス: dog
信頼度: 0.952
Box: [450.1, 200.3, 620.5, 450.2]
物体 3:
クラス: car
信頼度: 0.887
Box: [10.2, 350.1, 250.8, 475.9]
物体 4:
クラス: bicycle
信頼度: 0.823
Box: [300.5, 100.2, 500.3, 400.8]
物体 5:
クラス: chair
信頼度: 0.751
Box: [50.1, 300.5, 150.8, 450.2]
5-4. 結果の可視化
# ===================================================
# 6. 可視化関数
# ===================================================
def visualize_instance_segmentation(
image, boxes, labels, masks, scores,
class_names, mask_threshold=0.5
):
“””
インスタンスセグメンテーションの結果を可視化
Args:
image: PIL Image(元画像)
boxes: Bounding Box [N, 4]
labels: クラスラベル [N]
masks: マスク [N, 1, H, W]
scores: 信頼度 [N]
class_names: クラス名のリスト
mask_threshold: マスクの二値化閾値
“””
# figureを作成
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
ax.imshow(image)
# ランダムな色を生成(物体ごとに異なる色)
colors = plt.cm.tab20(np.linspace(0, 1, len(boxes)))
for i, (box, label, mask, score, color) in enumerate(zip(
boxes, labels, masks, scores, colors
)):
# —————————
# Bounding Boxの描画
# —————————
x1, y1, x2, y2 = box.cpu().numpy()
# 矩形を描画
rect = plt.Rectangle(
(x1, y1), # 左上の座標
x2 – x1, # 幅
y2 – y1, # 高さ
fill=False, # 塗りつぶしなし
edgecolor=color, # 枠線の色
linewidth=2 # 枠線の太さ
)
ax.add_patch(rect)
# —————————
# クラス名と信頼度のラベル
# —————————
class_name = class_names[label]
ax.text(
x1, y1 – 5, # テキストの位置
f'{class_name}: {score:.2f}’, # 表示テキスト
fontsize=10,
color=’white’,
bbox=dict(facecolor=color, alpha=0.7) # 背景色
)
# —————————
# マスクの描画
# —————————
# マスクを取得してNumPy配列に変換
mask_np = mask[0].cpu().numpy() # [1, H, W] → [H, W]
# 二値化(閾値以上を1、未満を0)
mask_binary = mask_np > mask_threshold
# 半透明のカラーマスクを作成
colored_mask = np.zeros((*mask_binary.shape, 4)) # RGBA
colored_mask[mask_binary] = [*color[:3], 0.5] # 物体部分に色
# マスクを重ねて表示
ax.imshow(colored_mask)
ax.axis(‘off’)
plt.tight_layout()
plt.savefig(‘result_instance_segmentation.png’, dpi=150, bbox_inches=’tight’)
plt.show()
# 可視化を実行
visualize_instance_segmentation(
original_image,
filtered_boxes,
filtered_labels,
filtered_masks,
filtered_scores,
COCO_CLASSES
)
print(“結果を保存しました: result_instance_segmentation.png”)
5-5. マスクの個別保存とMask IoUの計算
# ===================================================
# 7. マスクを個別に保存
# ===================================================
for i, (label, mask) in enumerate(zip(filtered_labels, filtered_masks)):
class_name = COCO_CLASSES[label]
# マスクを二値化
mask_binary = mask[0].cpu().numpy() > 0.5
# 0/1を0/255に変換してPIL Imageに
mask_image = Image.fromarray((mask_binary * 255).astype(np.uint8))
# ファイル名を作成して保存
filename = f’mask_{i+1}_{class_name}.png’
mask_image.save(filename)
print(f”マスク {i+1} を保存: {filename}”)
# ===================================================
# 8. Mask IoUの計算関数
# ===================================================
def compute_mask_iou(pred_mask, gt_mask):
“””
Mask IoUを計算
Args:
pred_mask: 予測マスク(NumPy配列、0〜1または0/1)
gt_mask: 正解マスク(NumPy配列、0〜1または0/1)
Returns:
iou: IoU値(0〜1)
“””
# 二値化(0.5を閾値)
pred_binary = pred_mask > 0.5
gt_binary = gt_mask > 0.5
# 交差(AND)と和集合(OR)
intersection = (pred_binary & gt_binary).sum()
union = (pred_binary | gt_binary).sum()
# ゼロ除算を防ぐ
if union == 0:
return 0.0
iou = intersection / union
return float(iou)
# 使用例(正解マスクがある場合)
# gt_mask = np.array(Image.open(‘ground_truth_mask.png’)) / 255.0
# pred_mask = filtered_masks[0, 0].cpu().numpy()
# iou = compute_mask_iou(pred_mask, gt_mask)
# print(f”Mask IoU: {iou:.3f}”)
# ===================================================
# 9. Dice係数の計算関数
# ===================================================
def compute_dice(pred_mask, gt_mask):
“””
Dice係数を計算
Args:
pred_mask: 予測マスク
gt_mask: 正解マスク
Returns:
dice: Dice係数(0〜1)
“””
pred_binary = pred_mask > 0.5
gt_binary = gt_mask > 0.5
intersection = (pred_binary & gt_binary).sum()
# Dice = 2 * 交差 / (予測 + 正解)
dice = 2 * intersection / (pred_binary.sum() + gt_binary.sum() + 1e-8)
return float(dice)
print(“\n推論完了!”)
実行結果:
マスク 1 を保存: mask_1_person.png
マスク 2 を保存: mask_2_dog.png
マスク 3 を保存: mask_3_car.png
マスク 4 を保存: mask_4_bicycle.png
マスク 5 を保存: mask_5_chair.png
推論完了!
💡 実装のポイントまとめ
1. モデル:
・maskrcnn_resnet50_fpn: ResNet-50 + FPN + RoIAlign
・pretrained=True: COCO学習済み(80クラス)
2. 出力:
・boxes: Bounding Box [N, 4]
・labels: クラスID [N]
・scores: 信頼度 [N]
・masks: マスク [N, 1, H, W](0〜1の確率)
3. 後処理:
・信頼度閾値(0.7など)でフィルタリング
・マスクは0.5で二値化
📝 練習問題
問題1:セマンティック vs インスタンス(基礎)
3台の車と5人の歩行者が写っている画像について、セマンティックセグメンテーションとインスタンスセグメンテーションの出力の違いを具体的に説明してください。
解答:
セマンティックセグメンテーション:
出力: クラスマップ(H×W)
各ピクセルにクラスID:
・車のピクセル → クラス「車」(3台すべて同じ)
・歩行者のピクセル → クラス「人」(5人すべて同じ)
・その他 → クラス「背景」「道路」など
結果の例:
┌─────────────────┐
│ 車 車 人人人 │ 3台の車を区別しない
│ 車車車 人人人人│ 5人の歩行者を区別しない
│ 道路 │
└─────────────────┘
特徴:
・物体の種類は分かる
・個数は分からない(「車がある」だけ)
・重なっている物体は分離できない
インスタンスセグメンテーション:
出力: 物体ごとのマスク(8個)
検出物体:
・物体1: クラス=車、マスク1(車A)
・物体2: クラス=車、マスク2(車B)
・物体3: クラス=車、マスク3(車C)
・物体4: クラス=人、マスク4(歩行者1)
・物体5: クラス=人、マスク5(歩行者2)
・物体6: クラス=人、マスク6(歩行者3)
・物体7: クラス=人、マスク7(歩行者4)
・物体8: クラス=人、マスク8(歩行者5)
結果の例:
┌─────────────────┐
│ 車A 車B 人1人2人3│ 各物体を個別識別
│ 車C車C 人4人4人5│ 重なっても分離可能
│ (なし) │ 背景はラベルなし
└─────────────────┘
特徴:
・「車が3台、人が5人」と分かる
・各物体を追跡可能
・重なっている物体も分離
問題2:RoIAlignの利点(中級)
RoI PoolingとRoIAlignの違いを、マスク予測への影響の観点から説明してください。stride=16の場合、RoI Poolingでどの程度の位置誤差が発生しうるか計算してください。
解答:
RoI Poolingの処理:
1. 座標変換(元画像 → 特徴マップ)
例: (124, 87) → (124/16, 87/16) = (7.75, 5.4375)
2. 量子化(整数に丸め)
(7.75, 5.4375) → (7, 5)
誤差:
・x方向: 0.75
・y方向: 0.4375
3. 元画像での誤差(stride=16を掛け戻す)
・x方向: 0.75 × 16 = 12ピクセル
・y方向: 0.4375 × 16 = 7ピクセル
最大誤差:
量子化で最大0.5ずれる可能性
→ 元画像で 0.5 × 16 = 8ピクセルの誤差
RoIAlignの処理:
1. 座標変換(同じ)
(124, 87) → (7.75, 5.4375)
2. 量子化しない
小数のまま保持: (7.75, 5.4375)
3. 双線形補間で値を計算
周囲4点から重み付け平均
誤差: なし(理論上)
マスク予測への影響:
RoI Pooling使用時:
・境界が最大8ピクセルずれる
・マスクの輪郭がぼやける
・特に小さい物体で影響大
・Mask APが約3%低下
RoIAlign使用時:
・境界が正確
・マスクの輪郭が鮮明
・小さい物体も正確にセグメント
結論:
インスタンスセグメンテーションにはRoIAlignが必須
問題3:YOLACTの高速化原理(中級)
YOLACTがMask R-CNNより高速な理由を、プロトタイプマスクと線形結合の仕組みを使って説明してください。
解答:
Mask R-CNNが遅い理由:
処理フロー:
1. RPN: 約2000個の候補領域を提案
2. RoIAlign: 各候補領域から特徴抽出
3. マスクヘッド: 各候補領域でマスク予測
ボトルネック:
・候補領域ごとにマスクを計算(逐次的)
・候補が多いと処理時間が増加
・並列化が難しい
計算量のイメージ:
1000個の候補 × マスク予測 = 1000回のマスク計算
YOLACTが速い理由:
処理フロー(並列化):
1. Protonet(画像全体で1回):
・k=32個のプロトタイプマスクを生成
・サイズ: 138×138×32
・画像全体で1回だけ計算
2. Prediction Head(並列処理):
・各物体でk=32個の係数を予測
・係数はスカラー値の集まり(軽量)
・すべての物体で同時に計算可能
3. Assembly(行列演算で高速):
M = σ(P × C^T)
P: プロトタイプ (138×138, 32)
C: 全物体の係数 (N, 32)
→ 1回の行列演算でN個のマスクを同時生成
計算量のイメージ:
1回のプロトタイプ生成 + N回の係数予測 + 1回の行列演算
→ Mask R-CNNの「N回のマスク計算」が
「1回の行列演算」に置き換わる
具体例:
左上にある物体のマスク生成:
プロトタイプ:
P1 = 左側強調、P2 = 右側強調、P3 = 上側強調、…
係数:
C = [0.8, 0.1, 0.7, 0.2, …]
最終マスク:
M = σ(0.8×P1 + 0.1×P2 + 0.7×P3 + …)
= 左上を強調したマスク
→ k個の係数(32個の数値)を予測するだけで
複雑なマスクを表現可能
問題4:Mask IoUとDice係数の計算(応用)
以下の条件でMask IoUとDice係数を計算し、両者の関係を確認してください。
予測マスク: 物体のピクセル数 = 180個
正解マスク: 物体のピクセル数 = 220個
重なる部分: 140個
解答:
1. Mask IoUの計算:
与えられた情報:
・予測マスク(A)= 180ピクセル
・正解マスク(B)= 220ピクセル
・交差(A ∩ B)= 140ピクセル
和集合の計算:
A ∪ B = A + B – (A ∩ B)
= 180 + 220 – 140
= 260ピクセル
IoUの計算:
IoU = |A ∩ B| / |A ∪ B|
= 140 / 260
= 0.538
= 53.8%
2. Dice係数の計算:
Dice = 2 × |A ∩ B| / (|A| + |B|)
= 2 × 140 / (180 + 220)
= 280 / 400
= 0.70
= 70.0%
3. 関係式の確認:
IoUからDiceを計算:
Dice = 2 × IoU / (IoU + 1)
= 2 × 0.538 / (0.538 + 1)
= 1.076 / 1.538
= 0.70 ✓
DiceからIoUを計算:
IoU = Dice / (2 – Dice)
= 0.70 / (2 – 0.70)
= 0.70 / 1.30
= 0.538 ✓
4. 結果の解釈:
Mask IoU = 53.8%:
・COCO評価でAP@0.5の基準(IoU≥0.5)は満たす
・AP@0.75の基準(IoU≥0.75)は満たさない
・改善の余地あり
Dice係数 = 70.0%:
・IoUより高い値(常にDice > IoU)
・医療画像では一般的な評価指標
予測の分析:
・過剰検出: 40ピクセル(180 – 140)
・見逃し: 80ピクセル(220 – 140)
・見逃しの方が多い → Recallを改善すべき
📝 STEP 14 のまとめ
✅ このステップで学んだこと
1. インスタンスセグメンテーション
・物体ごとに分離してマスクを生成
・セマンティック: クラス分類、インスタンス: 個体識別
・物体検出 + セグメンテーションの組み合わせ
2. Mask R-CNN
・Faster R-CNN + マスク分岐
・RoIAlign: 量子化なしで位置精度向上
・損失: L_cls + L_box + L_mask
3. YOLACT
・プロトタイプマスクの線形結合
・並列処理で高速化(約33 FPS)
・速度と精度のトレードオフ
4. 評価指標
・Mask IoU: ピクセル単位の重なり
・Mask AP: 複数IoU閾値での平均
・Mask AP < Box AP(マスクの方が難しい)
💡 重要ポイント
インスタンスセグメンテーションは、物体の個数カウントや個別追跡に必須です。Mask R-CNNは精度重視、YOLACTは速度重視のモデルです。RoIAlignはピクセル単位の精度に不可欠な技術です。
次のSTEP 15では、「パノプティックセグメンテーション」を学びます。セマンティック + インスタンスを統合し、画像内のすべてのピクセルをラベル付けする技術を習得します。