Skip to content

OCR: MPSが選択されず + DocumentAnalyzerをページ毎に再ロード — 実測7.1倍の改善余地 #68

Description

@clearclown

概要

Apple Silicon 上で YomiToku OCR が GPU (MPS) を一度も使わず CPU で実行されている。加えて DocumentAnalyzerページ毎にロードし直されている。両方を修正すると 1ページ 5.64s → 0.80s(7.1倍) になることを実測で確認した。出力は完全に同一。

README / RETROSPECTIVE.md には「YomiToku OCR(Apple Silicon MPS 加速)」と記載があるが、実装が伴っていない。

原因 1: MPS 分岐が存在しない

superbook-pdf/ai_bridge/yomitoku_bridge.py

# Configure device
if gpu_id is not None and torch is not None and torch.cuda.is_available():
    device = f"cuda:{gpu_id}"
else:
    device = "cpu"          # ← Apple Silicon では必ずここに落ちる

torch.cuda.is_available() は Mac では常に False のため、device は常に "cpu"。実機(M4 Max / macOS 26.6.1 / torch 2.13.0)で確認:

torch 2.13.0
cuda False
mps  True      ← 利用可能なのに選ばれない

YomiToku 本体は MPS に対応済み(yomitoku/base.pyelif "mps" in device: の分岐あり)。realesrgan_bridge.py も同じ torch.cuda.is_available() のみの判定になっている。

原因 2: ページ毎にモデルを再ロード

DocumentAnalyzer(device=device)process_image() の内部で毎回生成されている。superbook-pdf/src/markdown_pipeline.rs のページループが 1ページ = 1 Python プロセスで呼ぶため、600ページの書籍ではモデルを 600回 ロードすることになる。

実測(M4 Max, 590ページ書籍のページ画像 677KB)

工程 CPU MPS
import torch + yomitoku 1.13s 1.13s
モデルロード 2.76s 3.11s
推論(実計算) 1.66s 0.77s
合計 / ページ 5.64s 5.22s

MPS は推論を 2.2倍 速くしているが、ページ毎に払う約 4.2秒のオーバーヘッド(プロセス起動+モデルロード)に埋もれ、エンドツーエンドでは 8% しか改善しない。両方直して初めて効く。

修正後、10ページをディレクトリ指定で一括処理:

合計: 8.0s / 10ページ
1ページあたり: 0.80s
旧実装(5.64s/p)比: 7.1倍

出力の同一性(検証済み)

同一画像に対する text_blocks を比較:

修正前CPU == 修正後CPU : True   (--no-gpu が従来動作を維持)
修正前CPU == 修正後MPS : True   (MPS で結果が変わらない)

抽出テキスト例: 図17 企業価値-1>2であれば「儲かっている」

提案する修正

  1. デバイス選択を CUDA > MPS > CPU の優先順にgpu_id is None = --no-gpu の意味論は維持し、CUDA 経路は温存)
if gpu_id is not None and torch is not None and torch.cuda.is_available():
    device = f"cuda:{gpu_id}"
elif (
    gpu_id is not None
    and torch is not None
    and getattr(torch.backends, "mps", None) is not None
    and torch.backends.mps.is_available()
):
    device = "mps"
else:
    device = "cpu"
  1. DocumentAnalyzer をプロセス内でキャッシュ(デバイス毎に1回だけ生成)

  2. Rust 側でページをバッチ渡しにする — ブリッジ側は上記2で複数ページ対応済みだが、markdown_pipeline.rs が1ページずつ別プロセスで呼ぶ限り 4.2秒/ページのオーバーヘッドは残る。ここが残作業。

  3. realesrgan_bridge.py にも同様の MPS 分岐が必要。ただし MPS + fp16 は不安定なため、MPS 選択時は half=False を強制するのが安全。

影響

66冊のバッチ(平均37.7分/冊、CONVERT_PARALLEL=2)は現状 約20.7時間。1と2に加えて3を入れると 約2.9時間 になる見込み。

環境

  • Mac Studio (Apple M4 Max, 36GB, GPU 32コア), macOS 26.6.1
  • torch 2.13.0 / torchvision 0.28.0 / YomiToku 0.13.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions