0
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?

レシートをローカルLLMでマネーフォワードCSVまで作る前に知っておきたい4つの失敗

0
Posted at

レシートをローカルLLMでマネーフォワードCSVまで作る前に知っておきたい4つの失敗

レシート画像をローカルLLMで読み取り、マネーフォワード(会計ソフト)のインポート用CSVまで自動生成するシステムを構築しました。

この記事では、Phase 1(OCR + テキストLLM方式)で実際にハマった4つの失敗を紹介します。同じことを試そうとしている方の参考になれば幸いです。


この記事で伝えること

  • レシート画像 → ローカルLLM(OCR + テキストLLM)→ マネーフォワードCSV を試した結果の教訓
  • 同じことをやる人向けの「やっておくべきこと/避けるべきこと」
  • 技術選定の判断基準

アンチパターン①: OCRの誤認識をLLMのプロンプトでカバーしようとする

現象

PaddleOCRでレシートを読み取った際、以下のような誤認識が発生しました:

実際の文字 OCR結果 影響
¥108 半106車王 キ106 金額が完全に崩壊
¥ 合計金額の誤認識
¥ 消費税の誤認識
¥2,717 半2,717 金額の先頭文字が誤認識

これらの誤認識テキストをそのままLLMに渡した結果:

{
  "store_name": "FamilyMart",
  "date": "2024-01-05",
  "time": "09:38",
  "items": [{"name": "青NO", "price": "999"}],
  "total": "4497",  // 実際は108
  "tax": "5248",    // 実際は8
  "payment_method": "クレジット"
}

実際の金額は108円(税込)だったのに、4,497円と認識されてしまいました。

なぜ失敗したか

  1. プロンプトで誤認識を修正しようとした

    • ¥の誤認識の可能性がある」とプロンプトに書いた
    • しかし、LLMは文脈から推測するしかなく、確実な修正はできない
  2. 2段階処理で誤差が累積

    画像 → OCR → テキスト → LLM → JSON
           ❌誤認識      ❌誤解釈
    
    • OCRの誤認識がLLMの誤解釈を引き起こす
    • レイアウト情報(どこに何が書かれているか)が失われる

推奨: 入力品質を先に固める

  • OCR精度を上げる: 画像の前処理(コントラスト調整、ノイズ除去)を実施
  • OCRをスキップする: Vision LLMで画像から直接構造化データを抽出(Phase 2で採用)
  • 確実な修正ルール: LLMに期待せず、プログラムで確実に修正する
# 推奨: OCR誤字の確実な修正(プロンプトに頼らない)
def correct_ocr_errors(text):
    """OCR誤字を確実に修正"""
    corrections = {
        '': '¥',
        '': '¥',
        '': '¥',
    }
    for wrong, correct in corrections.items():
        text = text.replace(wrong, correct)
    return text

アンチパターン②: 「表示サイズ3GB」=「8GB PCで動く」と思い込む

現象

Vision LLMモデルを選定する際、以下のような誤解をしていました:

モデル 表示サイズ 実際のメモリ要求 結果
qwen2.5vl:3b 約3GB 8.4GB以上 ❌ 8GB RAMで落ちる
qwen2.5vl:7b 約7GB 12GB以上 ❌ 8GB RAMで落ちる

「3GBのモデルだから8GB PCで動く」と思い込んでいましたが、実際には実行時メモリが8GBを超えてエラーになりました。

エラーメッセージ例

RuntimeError: CUDA out of memory. Tried to allocate 2.5GB (GPU 0; 4.0GB total capacity; 1.2GB already allocated; 2.8GB free; 4.0GB reserved in total by PyTorch)

なぜ失敗したか

  1. 表示サイズ ≠ 実行時メモリ

    • モデルファイルのサイズと、実際にメモリに展開されるサイズは異なる
    • 推論時の一時メモリも必要
  2. 量子化の重要性を理解していなかった

    • llama3.2-vision:11b(Q4_K_M量子化)なら8GB RAMで動作
    • 量子化により、モデルサイズを約1/4に圧縮できる

推奨: 量子化と実機検証を必ず実施

  • 量子化モデルを使用: Q4_K_M、Q8_0などの量子化版を選ぶ
  • 実機で検証: 実際のPC環境でメモリ使用量を確認する
  • 余裕を持った選定: 表示サイズの2-3倍のメモリが必要と考える
# 推奨: 量子化モデルを使用
ollama pull llama3.2-vision:11b-q4_k_m  # 量子化版

アンチパターン③: 細長いレシートをそのままVision LLMに渡す

現象

コンビニの細長いレシート(アスペクト比が大きい)を処理した際、以下の問題が発生しました:

画像サイズ アスペクト比(高さ/幅) 結果
314×1836 5.85 ❌ JSONエラー
400×1200 3.0 ⚠️ 処理時間5分以上
600×1800 3.0 ❌ ハング(応答なし)

アスペクト比が3以上の細長いレシートで、JSONエラーまたは長時間のハングが発生しました。

エラーメッセージ例

Error: JSON parsing failed. Model output was not valid JSON.

または、処理が5分以上かかり、最終的にタイムアウト。

なぜ失敗したか

  1. Vision LLMは「普通の写真」用に学習されている

    • レシートのような細長い画像は想定外
    • アスペクト比が極端だと、モデルが混乱する
  2. 前処理をしていなかった

    • 画像をそのままモデルに渡していた
    • パディングやリサイズなどの前処理が必要

推奨: 前処理で比率を抑える

  • アスペクト比の調整: 3:4や4:3などの標準比率にパディング
  • 画像の分割: 細長いレシートは上下に分割して処理
  • ドメイン特化のファインチューニング: レシート専用モデルを作る(Phase 3で予定)
# 推奨: アスペクト比の調整
from PIL import Image

def adjust_aspect_ratio(image_path, target_ratio=0.75):
    """アスペクト比を調整(パディング追加)"""
    img = Image.open(image_path)
    width, height = img.size
    current_ratio = height / width
    
    if current_ratio > target_ratio:
        # 高さが長すぎる場合、左右にパディング
        new_width = int(height / target_ratio)
        new_img = Image.new('RGB', (new_width, height), (255, 255, 255))
        new_img.paste(img, ((new_width - width) // 2, 0))
        return new_img
    return img

アンチパターン④: 「ローカル=無料=正義」で終わりにしない

現象

「全部ローカルで完結させたい」という理想を追い求めましたが、現実は以下の通りでした:

項目 ローカルVision LLM Google Vision API(無料枠)
処理時間 1枚30秒〜5分 1枚1-3秒
月100枚の処理時間 約8時間以上 約3-5分
速度差 - 約500倍速
コスト 電気代のみ 月1,000枚まで無料
精度 金額30-40%(素のモデル) 95%以上

月100枚のレシートを処理するのに、ローカルでは8時間以上かかることが判明しました。

なぜ失敗したか

  1. 処理時間を軽視していた

    • 「動けばいい」と思っていた
    • しかし、実務では「待てる時間」が重要
  2. 無料APIの存在を無視していた

    • Google Vision APIは月1,000枚まで無料
    • 無料枠内で速度・精度が圧倒的に高い
  3. 「ローカル必須」の要件を再確認していなかった

    • プライバシー要件が本当に必要か?
    • 無料枠で足りるなら、クラウドも選択肢に入れるべき

推奨: 要件で判断基準を決める

  • ベースラインを数字で取る: 店舗名・日付・金額の正解率、1枚あたり時間を測定
  • 要件を明確にする: 「ローカル必須か」「無料枠で足りるか」を要件で決める
  • 表で比較する: コスト・速度・精度を表にして、判断材料にする
判断基準 ローカルVision LLM クラウドAPI
プライバシー要件が厳しい ✅ 推奨 ❌ 不向き
処理速度を重視 ❌ 不向き ✅ 推奨
月100枚以下 ⚠️ 検討 ✅ 推奨
月1,000枚以上 ⚠️ 検討 ⚠️ 有料

やるべきこと・判断の目安

1. ベースラインを数字で取る

測定すべき項目:

  • 店舗名の正解率
  • 日付の正解率
  • 金額の正解率(最重要)
  • 1枚あたりの処理時間

測定結果の例(Phase 1の結果):

項目 正解率 備考
店舗名 80% 候補から選択できている
日付 95% ほぼ正確
金額 30-40% 実用不可
消費税 70% 誤字が残る

2. プロンプトとファインチューニングの役割を分けて考える

  • プロンプト = 応急処置: 簡単な調整は可能だが、根本的な精度向上は難しい
  • ファインチューニング = 根本治療: 実用的な精度を出すには、専用モデルの学習が必要

3. 「ローカル必須か」「無料枠で足りるか」を要件で決める

判断フローチャート:

プライバシー要件が厳しい?
├─ Yes → ローカル必須(Vision LLM + ファインチューニング)
└─ No → 無料枠で足りる?
    ├─ Yes → クラウドAPI(Google Vision API等)
    └─ No → ローカル + クラウドのハイブリッド

まとめ: 技術選定チェックリスト

レシートをローカルLLMで処理する前に、以下を確認してください:

ハードウェア要件

  • メモリ: 8GB以上(量子化モデル使用時)
  • GPU: 必須ではないが、あると処理速度が向上
  • ストレージ: モデルファイル用に5-10GBの空き容量

処理要件

  • 処理枚数: 月何枚を想定しているか
  • 待てる時間: 1枚あたり何秒まで許容できるか
  • 精度要件: 金額の正解率は何%必要か

プライバシー要件

  • ローカル必須か: データを外部に送信できないか
  • 無料枠で足りるか: 月1,000枚以下なら無料APIが使える

技術選定の選択肢

選択肢 向いているケース
ローカルVision LLM プライバシー要件が厳しい、月100枚以下
クラウドOCR + ローカルLLM プライバシー要件は緩いが、LLMはローカルで
クラウドAPI中心 処理速度を重視、無料枠で足りる

おわりに

Phase 1(OCR + テキストLLM方式)では、上記の4つの失敗を経験しました。

現在はGoogle Vision API(OCR) + Ollama(ローカルLLM)のハイブリッド構成に移行し、実用的な速度と精度を実現しています。

「全部ローカルで」という理想は素晴らしいですが、ユーザー体験を優先して技術選定することが重要だと学びました。

同じことを試そうとしている方の参考になれば幸いです。


参考リンク


次回予定: Phase 2(Vision LLM方式)の検証結果も記事にする予定です。

0
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
0
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?