1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

手探りしてみる CV/ ML/ NN: 10日目 sam3を深掘りする話3

Last updated at Posted at 2025-12-10

SAM3 深掘り総括 - 設計思想から実装まで

前々回でDetector、前回でTrackerを深掘りしてきました。最終回となる今回は、これまでの知見を総括し、SAM3の設計思想、実装のポイント、そして今後への展望をまとめます。


1. 振り返り:最初の疑問から

全ては 「なぜVideoでは288×288がそのままリサイズされるだけなのか?」 という疑問から始まりました。

Image処理:Detector → 1008×1008(Learned PixelDecoder/FPN)
Video処理:Tracker → 288×288(Shared ViT + Tracker Neck)

この疑問を追っていく中で、SAM3の設計思想が見えてきました。


2. 設計思想の考察

2.1 なぜTrackerは288×288なのか?

答え:タスクの難易度が違うから

[Detector の仕事]
「この画像に何がある?どこにある?」
→ 問題を解く(Problem Solving)
→ 高解像度特徴 + 学習可能なアップサンプリングが必要

[Tracker の仕事]
「前のフレームのこれ、次のフレームではどこ?」
→ 答えを確認する(Answer Verification)
→ 低解像度でも十分、シンプルなF.interpolateで OK

具体例で考える

プロンプトフレーム(初回):

入力:画像 + テキスト"person"
Detector の仕事:
  - この画像のどこに人がいるか探す(難しい!)
  - ViT特徴(1008×1008)で探索
  - Learned Queriesで100箇所チェック
  - PixelDecoder/FPNで高品質マスク生成

次のフレーム(追跡):

入力:画像 + 前フレームのマスク位置
Tracker の仕事:
  - 「さっきここにいた人、今どこ?」(比較的簡単)
  - ViTから抽出したTracker用特徴(288×288)で十分
  - 前のメモリ(6フレーム)から予測
  - F.interpolateでリサイズで OK

計算コスト vs 品質のトレードオフ


Gemini_Generated_Image_vrkrl4vrkrl4vrkr.png

Detector Tracker
タスク 検出(難) 追跡(易)
解像度 1008×1008 288×288
アップサンプリング Learned (FPN) F.interpolate
バックボーン ViT (Shared) ViT (Shared)
特徴抽出 PixelDecoder Tracker Neck
計算コスト 低(Neck以降)
品質 最高品質必須 十分な品質

Trackerが毎フレーム動くことを考えると、288×288は賢い選択です。

2.2 なぜDetectorとTrackerを分けたのか?

答え:役割が根本的に違うから

情報の流れの違い

[Detector]
テキスト"person" ──┐
                  ├→ 意味的マッチング → マスク生成
画像特徴 ──────────┘
(新しい概念を理解する必要がある)

[Tracker]
前フレームマスク ───┐
                 ├→ 幾何学的マッチング → マスク伝播
画像特徴 ─────────┘
(形と位置を追うだけ)

統合モデルにしなかった理由

もし統合したら:

# 仮想的な統合モデル
unified_model(
    image,
    text_prompt,      # 毎フレーム処理(重い)
    prev_masks,       # メモリ管理が複雑
    geometric_prompt
)
# → テキスト処理のオーバーヘッドが毎フレーム発生
# → メモリ機構とテキスト理解の干渉
# → デバッグ・改善が困難

分離することで:

  • Detector: テキスト理解に特化、新規検出に集中、
  • Tracker: 時間的一貫性に特化、メモリ機構を最適化
  • 統合レイヤー: IoUマッチングでシンプルに結合

モジュール設計の美学ですね。
合わせてDetectorはそれ単体でマスクを生成できるので単体でもend-to-endの学習可能です。


3. SAM1 → SAM2 → SAM3 の進化

進化の系譜

[SAM1] (2023)
- 画像のみ
- プロンプト:点、ボックス、マスク
- セマンティック理解なし

[SAM2] (2024)
- ビデオ追加
- メモリ機構導入
- 時間的一貫性
- まだテキストなし

[SAM3] (2025)
- オープンボキャブラリー
- Detector + Tracker 統合
- テキスト + 幾何学 + Exemplar
- 270K概念対応

SAM3の革新性

1.概念レベルのプロンプト

SAM1/2: 「ここをセグメント」(位置指定)
SAM3:   「personをすべてセグメント」(概念指定)

*加えてSAM3はインスタンスセグメンテーションをしています。

2.統合アーキテクチャ

Image: Detector のみ
Video: Detector + Tracker(デュアルバックボーン)

3.スケーラビリティ

Learned Queries: 100スロットで並列検出
メモリバンク: 6フレーム + 16ポインター

4. Image vs Video 処理の違い総まとめ

4.1 処理フローの比較

Image処理:

入力画像
  ↓
ViTバックボーン(1008×1008)
  ↓
Detector
  ├─ テキスト特徴とクロスアテンション
  ├─ Learned Queries(100スロット)
  └─ PixelDecoder/FPN(学習済み)
  ↓
出力マスク [N, 1008, 1008]

Video処理:

各フレーム
  ↓
┌─────────────────┬─────────────────┐
│   Detector      │    Tracker      │
│  (ViT 1008)     │ (ViT -> 288)    │
├─────────────────┼─────────────────┤
│ テキスト使う      │ テキスト使わない   │
│ 新規検出         │ 既存追跡          │
│ 学習済みFPN      │ F.interpolate   │
└────────────────┴──────────────────┘
  ↓              ↓
  新規マスク      既存マスク
       ↓        ↓
    IoUマッチング(N×M)
       ↓
  統合マスク [M, 1008, 1008]
  (obj_id付き)

Gemini_Generated_Image_wxhnufwxhnufwxhn.png

4.2 使い分けの指針

シーン 推奨 理由
単一画像 Image 高品質、シンプル
短い動画(<10フレーム) Image(各フレーム) オーバーヘッド小
長い動画 Video メモリ機構で一貫性
リアルタイム Video Tracker軽量
高品質静止画 Image Detector最大活用

5. 実装・カスタマイズ例

5.1 マスク統合の実装

前回少し出ていた、Reconditioning部分には幾分か改良案があります。例えば新しく検出されたおそらく同一インスタンス(IoUで判断)をmaxで追加していくなど:

# sam3/model/sam3_video_base.py の build_outputs()

# Part 3: Reconditioningの改造
if reconditioned_obj_ids is not None and len(reconditioned_obj_ids) > 0:
    for obj_id in reconditioned_obj_ids:
        det_idx = trk_id_to_max_iou_high_conf_det.get(obj_id)

        if det_idx is not None:
            # Detectorマスク取得
            det_mask = det_out["mask"][det_idx]
            det_mask_resized = F.interpolate(
                det_mask.unsqueeze(0).unsqueeze(0).float(),
                size=(orig_vid_height, orig_vid_width),
                mode="bilinear",
                align_corners=False,
            ).squeeze(0)

            # 既存のTrackerマスク
            trk_mask = obj_id_to_mask[obj_id]

            # === ここを変更 ===
            # 元:置き換え
            # obj_id_to_mask[obj_id] = det_mask_resized

            # 新:max統合
            obj_id_to_mask[obj_id] = torch.max(
                trk_mask.float(),
                det_mask_resized
            ) > 0

            # または:加算統合(要正規化)
            # combined = (trk_mask.float() + det_mask_resized) / 2
            # obj_id_to_mask[obj_id] = combined > 0.5

効果

  • Trackerの時間的一貫性 + Detectorの高品質
  • エッジのガタつき軽減の可能性
    自分でも試してみましたが、この後5.4で述べてるようにReconditioningがデフォルト値のままだと効果は分かりづらいので適宜そちらも変更する必要があります。

5.2 カスタムマッチング戦略

IoU以外のマッチング戦略:

def custom_matching(det_masks, trk_masks, det_scores, trk_scores):
    """
    カスタムマッチング:IoU + 中心距離 + スコア加重
    """
    N, M = len(det_masks), len(trk_masks)
    scores = torch.zeros(N, M)

    for i, det_mask in enumerate(det_masks):
        for j, trk_mask in enumerate(trk_masks):
            # IoU
            iou = compute_iou(det_mask, trk_mask)

            # 中心距離(正規化)
            det_center = get_mask_center(det_mask)
            trk_center = get_mask_center(trk_mask)
            dist = torch.norm(det_center - trk_center)
            dist_score = 1.0 / (1.0 + dist)

            # スコア加重
            conf_score = (det_scores[i] + trk_scores[j]) / 2

            # 統合スコア
            scores[i, j] = (
                0.5 * iou +
                0.3 * dist_score +
                0.2 * conf_score
            )

    # Hungarian algorithmでマッチング
    matches = hungarian_algorithm(scores)
    return matches

5.3 プロンプト戦略

効果的なプロンプトの使い方

# 戦略1: 階層的プロンプト
# 粗→細で段階的に
predictor.add_prompt(text="vehicle")      # 粗い概念
predictor.propagate(...)
predictor.add_prompt(text="car", frame_idx=10)  # 細かい概念

# 戦略2: ハイブリッドプロンプト
# テキスト + ポイントの組み合わせ
predictor.add_prompt(text="person")       # 自動検出
predictor.add_prompt(                      # 手動追加
    points=[[x, y]],
    obj_id=new_id,
    frame_idx=frame_idx
)

# 戦略3: リファインメント
# 粗いマスク → ポイントで修正
predictor.propagate(...)  # 自動追跡
# 間違いを発見
predictor.add_prompt(     # ポイントで修正
    points=[[x1, y1], [x2, y2]],
    point_labels=[1, 0],  # positive, negative
    obj_id=existing_id,
    frame_idx=error_frame
)

5.4 定期的なReconditioning - Trackerのリフレッシュ

長時間の追跡では、Trackerの予測が徐々にドリフト(誤差の蓄積)します。SAM3はこれを防ぐため、定期的にDetectorのマスクでTrackerを更新します。

デフォルト設定

# model_builder.py
recondition_every_nth_frame=16  # 16フレームごとにリフレッシュ

実行条件

# sam3_video_base.py
should_recondition_periodic = (
    self.recondition_every_nth_frame > 0          # 有効
    and frame_idx % self.recondition_every_nth_frame == 0  # 16の倍数
    and len(trk_id_to_max_iou_high_conf_det) > 0  # マッチあり
)

Reconditioningの処理

def _recondition_masklets(...):
    HIGH_CONF_THRESH = 0.8  # logits閾値(実際には比較的低め)

    for trk_obj_id, det_idx in trk_id_to_max_iou_high_conf_det.items():
        # 1. Detectorの新しいマスク取得
        new_mask = det_out["mask"][det_idx]

        # 2. Trackerのスコアが閾値以上かチェック
        obj_score = tracker_obj_scores_global[obj_idx]  # logits値
        if obj_score > HIGH_CONF_THRESH:
            # 3. Trackerの内部状態を更新!
            self.tracker.add_new_mask(
                inference_state=inference_state,
                frame_idx=frame_idx,
                obj_id=trk_obj_id,
                mask=new_mask_binary,  # Detectorのマスクで上書き
            )

            # 4. メモリエンコーダー再実行
            self.tracker.propagate_in_video_preflight(
                tracker_state, run_mem_encoder=True
            )

ポイント

  • obj_score > 0.8(logits)は比較的低い閾値
    • logitsは通常7〜8以上になる
    • つまり、ほとんどのオブジェクトがreconditioningの対象
  • DetectorのマスクでTrackerのメモリを直接更新
  • これにより、ドリフトを防ぎつつ時間的一貫性を維持

仕組みの全体像

フレーム 0: Detector検出 → Tracker初期化
フレーム 1-15: Trackerで追跡(誤差が少しずつ蓄積)
フレーム 16: Reconditioning
  ├─ Detector再検出
  ├─ Trackerとマッチング
  └─ Trackerのメモリをリフレッシュ
フレーム 17-31: Trackerで追跡(リセットされた状態から)
フレーム 32: Reconditioning
  ...

メリット

  • Trackerのドリフト防止
  • 長時間追跡の精度向上
  • Detectorの高品質を定期的に活用

設定のカスタマイズ

# より頻繁に(計算コスト増)
recondition_every_nth_frame=8

# 無効化(ablation study用)
recondition_every_nth_frame=0

# スコア閾値の調整
HIGH_CONF_THRESH = 2.0  # より厳しく

6. パフォーマンス考察

6.1 メモリ使用量

理論値(FP32、バッチサイズ1):

# Detector(毎フレーム)
ViT特徴: 1008×1008×256  1GB
Detector出力: 100×1008×1008  400MB

# Tracker(毎フレーム)
Tracker特徴: 288×288×256  85MB
メモリバンク: 6フレーム×obj数×特徴  可変
オブジェクトポインター: 16×256  16KB無視できる

# 合計(10オブジェクト追跡時)
 1.5GB + メモリバンク(〜500MB  2GB

最適化のヒント

  1. offload_output_to_cpu_for_eval=True:長い動画向け
  2. memory_temporal_stride_for_eval > 1:メモリフレーム間引き
  3. FP16/BF16:メモリ半減

6.2 計算ボトルネック

プロファイリング結果(推定):

Detector: 55%
  ├─ ViTバックボーン: 25% (Shared)
  ├─ Cross-Attention: 20%
  └─ PixelDecoder: 10%

Tracker: 35%
  ├─ Tracker Neck: 20%
  └─ Memory Attention: 15%

統合レイヤー: 10%
  └─ IoUマッチング: 10%

高速化のポイント

  • Detectorが支配的 → allow_new_detections=Falseで無効化(propagation時)
  • バッチ処理でスループット向上
  • コンパイル(torch.compile)

7. 未解決の疑問と展望

7.1 残った疑問

Q1: なぜSAM3はHieraではなくViTなのか?

SAM2では高速なHieraが採用されていました。なぜSAM3ではViTに戻ったのでしょうか?

推測

  • Detectorの精度優先: オープンボキャブラリー検出には強力なViTDetが必要
  • 統合の効率化: DetectorとTrackerでバックボーンを共有することで、メモリ効率と実装の単純さを優先(Dual BackboneだとVRAMが倍になる)
  • Hieraの役割: Tracker用の軽量な特徴量は、ViT出力からNeck(FPN)を通して生成することで模倣

Q2: Learned Queriesは100で十分?

最大100オブジェクトの制限、実用上問題ない?

考察

  • SA-Co評価では99.8%のケースで十分
  • 混雑シーンでは不足の可能性
  • 可変スロット数は今後の課題

Q3: メモリ選択戦略は最適?

固定6フレームより、動的選択の方が良い?

SAM2Longの試み

  • use_memory_selection=Trueオプション
  • 有効なフレームのみ選択
  • まだ研究段階

8. 総括

8.1 SAM3の設計思想

3回に渡るの深掘りから見えた設計思想:

1. タスク分離
   - Detector = Problem Solver
   - Tracker = Answer Verifier
   - 統合レイヤー = Simple Arbiter



2. **計算効率と品質のバランス**
   - Detector: 高コスト・高品質(必要な時だけ)
   - Tracker: 低コスト・十分品質(常時動作)
   

3. **モジュール性**
   - 各コンポーネント独立
   - 改善・デバッグが容易
   - 拡張性が高い

8.2 最後に

最初の疑問「なぜ288×288?」から始まった探求は、SAM3の設計思想の深い理解へと繋がりました。

288×288は単なる低解像度ではなく、
「追跡」というタスクに最適化された
賢い設計判断だった

SAM3は、画像とビデオのセグメンテーションを統合した、現時点での最先端モデルです。完璧ではありませんが、その設計思想とトレードオフは、今後のモデル開発の参考になるでしょう。

SAM4が出る頃には、また新しい発見があるはずです。楽しみですね!




参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?