PaddleOCR で画像を推論した瞬間、次の NotImplementedError で止まることがあります。
NotImplementedError: (Unimplemented) ConvertPirAttribute2RuntimeAttribute not support [pir::ArrayAttribute<pir::DoubleAttribute>] (at /paddle/paddle/fluid/framework/new_executor/instruction/onednn/onednn_instruction.cc:116)
PaddleOCR(...) の初期化やモデルのダウンロードは通るのに、predict()(古い API では ocr())で推論を回した瞬間に落ちるのが特徴です。検索でコピペする文字列は表示ゆれがあるので、代表的なものを並べます。
- 例外クラス:
NotImplementedError(メッセージ先頭に(Unimplemented)) - コード名:
ConvertPirAttribute2RuntimeAttribute not support - 未対応の属性型:
[pir::ArrayAttribute<pir::DoubleAttribute>] - 発生源として出るファイル:
new_executor/instruction/onednn/onednn_instruction.cc:116
結論を先に書くと、直し方は 2 通りあります。paddle を最新のままにして PaddleOCR(enable_mkldnn=False) で oneDNN を切る(変更が一番小さい)か、paddlepaddle を回帰前の 3.2.2 に下げるか。paddleocr は 3.5.0 のままで構いません。
# 案1: paddle 3.3.1 のまま、oneDNN を無効化する(1 引数追加するだけ)
engine = PaddleOCR(enable_mkldnn=False, ...)
# 案2: oneDNN の高速化を保ちたいなら、回帰前の版へ下げる
pip install "paddlepaddle==3.2.2"
どちらを採るにしても、まず落ちている原因(paddle の版と oneDNN 経路)を確かめてから選ぶのが早道です。
まず paddlepaddle のバージョンと oneDNN を確認する
この症状を決めるのは paddleocr ではなく、その下で実際に計算を回す paddlepaddle(実行エンジン)のバージョンと、CPU 高速化に使う oneDNN の有無です。同じ paddleocr==3.5.0 でも、組み合わせで結果が分かれます(すべて同じ画像・python:3.11 の CPU 環境で確認したもの)。
| paddlepaddle | 設定 | 同じ画像を OCR したとき |
|---|---|---|
| 3.3.1 | 既定(oneDNN 有効) | NotImplementedError で停止 |
| 3.3.1 | enable_mkldnn=False | 認識して終了コード 0 |
| 3.2.2 | 既定 | 同じく通る |
| 3.1.1 | 既定 | 通る(最古の動作版) |
いま入っている版はこの一行で見えます。
python -c "import paddle; print(paddle.__version__)"
3.3.1(または上流で同じ報告のある 3.3.0)が表示され、GPU ではなく CPU 推論なら、この記事の対象です。
「手元では動くのに CI で落ちる」理由
paddleocr(と、その中で推論を担う paddlex)は、依存パッケージに paddlepaddle を宣言していません。CPU 版か GPU 版か、CUDA の版はいくつかを利用者が選ぶ設計のため、実行エンジンは別途入れる形になっています。つまり paddle の版は、ベースイメージ・requirements.txt・手作業の pip install のどれか=依存解決の外で決まります。
ここから環境差が生まれます。版を固定せずに pip install paddlepaddle し直したクリーンな CI や別マシンは、その時点の最新(今なら 3.3.1)を引いて落ちる。一方、少し前に 3.1.x や 3.2.x を入れたまま動いている開発機では出ません。paddle の版をログに出していないと、「同じコードなのに CI だけ落ちる」ように見えます。
再現(最小構成)
適当な文字を書いた画像を 1 枚用意し、PP-OCRv5 の mobile モデルで推論します。引数は検証に使ったものをそのまま載せます。
# probe.py
from pathlib import Path
from PIL import Image, ImageDraw
from paddleocr import PaddleOCR
image_path = Path("sample.png")
img = Image.new("RGB", (360, 120), "white")
ImageDraw.Draw(img).text((24, 44), "ERRFIX 123", fill="black")
img.save(image_path)
engine = PaddleOCR(
text_detection_model_name="PP-OCRv5_mobile_det",
text_recognition_model_name="PP-OCRv5_mobile_rec",
text_det_limit_type="max",
text_det_limit_side_len=736,
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
cpu_threads=1,
)
result = engine.predict(str(image_path))
print(result)
paddlepaddle==3.3.1 の環境でこれを実行すると、engine.predict(...) の内側の検出モデル実行(predictor.run())で先ほどの NotImplementedError が投げられ、終了コードは 1 になります。エンジン生成(PaddleOCR(...))までは到達するので、モデル名や引数の設定ではなく実行時の問題だと切り分けられます。
解決1: paddle は最新のまま oneDNN を切る
落ちているのは oneDNN 向けの命令を組み立てる経路です。そこを使わなければこの変換に入りません。PaddleOCR(...) に enable_mkldnn=False を渡すと、paddlepaddle==3.3.1 のままで probe.py が最後まで通り、終了コード 0 になります。
engine = PaddleOCR(
text_detection_model_name="PP-OCRv5_mobile_det",
text_recognition_model_name="PP-OCRv5_mobile_rec",
enable_mkldnn=False, # この 1 行を足す
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
cpu_threads=1,
)
引き換えに、oneDNN による CPU 推論の高速化は失われます。CPU での処理が遅くて困るなら、次の版を下げる案を選んでください。なお、このエラー自体が CPU + oneDNN の経路に固有なので、GPU 推論では発生しません。
解決2: paddlepaddle を 3.2.2 に下げる
oneDNN の高速化を保ちたいときは、回帰が入る前の版へ下げます。PaddlePaddle 本体の Issue #77340 は、この例外を paddlepaddle 3.3.0 の回帰として報告し、回避版として 3.2.2 を挙げている。paddleocr==3.5.0 のまま 3.2.2 で同じ画像が通ることも確認済みです。
pip install "paddlepaddle==3.2.2"
すでに 3.3.1 が入っている環境なら、このコマンドで 3.2.2 へ入れ替わります。さらに古い 3.1.1 でも通るので、3.2.2 が別の依存の都合で使えないときのフォールバックにできます。
CI やチームで再発させないなら、requirements.txt に理由ごと固定します。issue が閉じた版が出たら、この行を外して最新で再確認する前提のコメントを添えておくと、古い版に貼り付いたまま忘れる事故を防げます。
paddleocr==3.5.0
# paddle 3.3.x は oneDNN + PIR の属性変換で NotImplementedError(Paddle Issue #77340)。
# 回帰前の版に固定。修正版が出たら外して最新で再確認する。
paddlepaddle==3.2.2
GPU 版(paddlepaddle-gpu)を使っている場合、3.x のホイールは PyPI に無く、公式のインデックス URL を指定して入れる形になります。上のコマンド(CPU 版 paddlepaddle)をそのまま実行すると CPU 版と GPU 版が同居して別の不具合を招くので、片方を pip uninstall してから入れ替えてください。ここで確認したのは CPU 推論の経路で、GPU 版で同じ例外が出るかは試していません。
なぜ oneDNN を切る・版を下げると直るのか
メッセージの ConvertPirAttribute2RuntimeAttribute と pir::... は、paddle の実行系 **PIR(Paddle Intermediate Representation)**の内部処理を指します。推論のとき、計算グラフに付いた属性を実行時の属性へ変換する段があり、paddlepaddle==3.3.1 はここでモデルが持つ「double の配列」型の属性(pir::ArrayAttribute<pir::DoubleAttribute>)を変換できず、NotImplementedError を投げます。落ちる場所が onednn_instruction.cc である点が示すとおり、これは oneDNN バックエンド向けの命令を組み立てる経路です。だから enable_mkldnn=False で oneDNN を外すとこの変換に入らず通り、回帰前の 3.2.2 以下でも通ります。
これはモデル側の不備ではなく、paddle の実行エンジン側の問題として扱われています。上流は paddlepaddle 3.3.0 の回帰として報告しており(Issue #77340)、同じ例外は PaddleOCR に限らず、CPU + oneDNN で paddle の推論を回す構成でも起きます。errfix で実測したのは 3.3.1(落ちる)/3.3.1 + enable_mkldnn=False(通る)/3.2.2(通る)/3.1.1(通る)の 4 点です。
切り分け
- paddle の版と CPU/GPU を先に見る: 落ちる環境と動く環境で、まず
paddlepaddleの版(python -c "import paddle; print(paddle.__version__)")と、CPU 推論か GPU 推論かを比べる。paddleocrの版より先にこちらを疑う。 - どこまで確認済みか: errfix が実測したのは上表の 4 点で、上流 Issue #77340 は 3.3.0 と 3.3.1 で報告している。3.3 系すべてや将来の修正版までは検証していないので、「3.3 なら必ず落ちる」と決めつけず、修正版が出たら最新で同じ画像が通るかを確かめる。
- OCR に入る前に読み込みで落ちる場合は別問題: 最小寄りの Linux / サーバー用イメージ(ここで検証したのは Debian 12 系の
python:3.11)では OpenCV が要求するlibGL.so.1が無く、paddleocrの import 時点で止まります。これは paddle の版ではなく OS パッケージの不足です。Debian 12 系ならapt-get install -y libgl1 libglib2.0-0、Debian 13 / Ubuntu 24.04 系ではパッケージ名がlibglib2.0-0t64に変わります。libGL.so.1自体を要求しないopencv-python-headlessに寄せる手もあります。