概要
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.py に elif "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であれば「儲かっている」
提案する修正
- デバイス選択を 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"
-
DocumentAnalyzer をプロセス内でキャッシュ(デバイス毎に1回だけ生成)
-
Rust 側でページをバッチ渡しにする — ブリッジ側は上記2で複数ページ対応済みだが、markdown_pipeline.rs が1ページずつ別プロセスで呼ぶ限り 4.2秒/ページのオーバーヘッドは残る。ここが残作業。
-
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
概要
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.pytorch.cuda.is_available()は Mac では常に False のため、deviceは常に"cpu"。実機(M4 Max / macOS 26.6.1 / torch 2.13.0)で確認:YomiToku 本体は MPS に対応済み(
yomitoku/base.pyにelif "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)
MPS は推論を 2.2倍 速くしているが、ページ毎に払う約 4.2秒のオーバーヘッド(プロセス起動+モデルロード)に埋もれ、エンドツーエンドでは 8% しか改善しない。両方直して初めて効く。
修正後、10ページをディレクトリ指定で一括処理:
出力の同一性(検証済み)
同一画像に対する text_blocks を比較:
抽出テキスト例:
図17 企業価値-1>2であれば「儲かっている」提案する修正
gpu_id is None=--no-gpuの意味論は維持し、CUDA 経路は温存)DocumentAnalyzerをプロセス内でキャッシュ(デバイス毎に1回だけ生成)Rust 側でページをバッチ渡しにする — ブリッジ側は上記2で複数ページ対応済みだが、
markdown_pipeline.rsが1ページずつ別プロセスで呼ぶ限り 4.2秒/ページのオーバーヘッドは残る。ここが残作業。realesrgan_bridge.pyにも同様の MPS 分岐が必要。ただし MPS + fp16 は不安定なため、MPS 選択時はhalf=Falseを強制するのが安全。影響
66冊のバッチ(平均37.7分/冊、
CONVERT_PARALLEL=2)は現状 約20.7時間。1と2に加えて3を入れると 約2.9時間 になる見込み。環境