判定用LLMをローカル機に降ろしてAPI枠を節約できるか実測した話(137本測定と見送り判断)

  • #LLM
  • #ローカルLLM
  • #Ollama
  • #ベンチマーク
  • #パイプライン最適化
  • #コスト削減
判定用LLMをローカル機に降ろしてAPI枠を節約できるか実測した話(137本測定と見送り判断)

今回やったこと / この記事について

日次で大量のコンテンツを処理する自動化パイプラインにおいて、クラウドLLM(定額サブスク枠)の消費を抑える目的で、選定判定の1段目(トピック分類・型分類)をローカルマシンの Ollama に移設できるか検証した。

実データ137本全数を用いてベンチマーク測定を実施したところ、以下のような実測値が得られた。

  • 型名の一致率は36%にとどまる: クラウドモデルが判定した型(19分類)と、ローカルLLM(gemma4)による判定の一致率は約36%(14/39本)にとどまり、型名を正確に当てるタスクとしては実用域に達しなかった。
  • 拾う/没の二値判定は79%: 一方で、コンテンツを「採用するか不採用(没)にするか」という粗いフィルタリングとしては79%の一致率を示し、採用すべき候補の取りこぼしは6%(1/18本)に抑えられた。
  • 送信文字数の削減効果は-14.9%: 既定の運用安全マージン(長文スレッドのみ1段目をローカル化)ではクラウドへの送信文字数の削減幅は14.9%にとどまった。前置きフィルタを全件に適用すれば最大52.5%削減できるものの、採用すべきコンテンツの16%を取りこぼすトレードオフが発生する。
  • 本人の作業機GPUを毎日25分占有: 4並列で処理しても137本の判定に約25分を要し、ローカル画像生成等の別タスクとGPUリソースが競合する。

総合的に判断した結果、「14.9%の枠削減のために作業機のGPUを毎日25分占有し、取りこぼしのリスクを抱えるのは割に合わない」と結論づけ、本線へのマージは見送りコードを取り下げた。

本記事では、この検証プロセスで判明した「安定(自己一致)と正しさは別物である」という教訓や、Ollama運用における具体的な落とし穴を記録する。

検証の背景:2段階判定パイプライン

運用中のキュレーション・要約システムでは、コンテンツの品質担保とトークン消費の抑制を目的に、判定処理を2段階に分割している。

段階判定内容入力データ出力データクラウドでの特性
1段目(型・話題分類)コンテンツの型(19択)、ジャンル、広告適合性OP(先頭文)+冒頭20レス小さなJSON形式同一入力15回で15回とも同一の型を出力(自己一致100%)
2段目(オチ・仮説抽出)スレッド全体の文脈把握、オチの特定、仮説検証全文(発言番号+ID+本文)詳細な検証レポート微妙な文脈判断を伴い、モデルによって成立率が変動

1段目の入力は短く、出力も定型分類である。さらに事前検証において「同じスレッドを15回判定させても15回すべて同じ型を出力する」という極めて高い再現性を示していた。

このことから、「1段目は分類タスクとして難易度が低く、ローカルLLMでも代替可能ではないか」「1段目で不採用となったスレッドを足切りできれば、2段目(全文送信)のクラウド送信トークンを大幅に節約できるはずだ」という仮説が立ち、検証を開始した。

137本全数測定の結果

Apple Silicon(M5 Pro / 64GBメモリ)上で Ollama を稼働させ、実データ138本(除外後137本)を対象に、過去にクラウド判定を行った正解ログとの突き合わせ評価を実施した。

評価スクリプトは過去の判定結果ログ(baseline)を参照し、クラウド側APIを一切呼び出さずにローカルのみで測定を完結させた。

1. 「1段目 vs 1段目」の公平な比較(res > 100・39本)

クラウド側でも1段目判定(OP+冒頭20レス)が実行された長文コンテンツにおける、モデル別の精度比較である。

評価指標gemma4:latestqwen3:30b-a3b(思考あり)
型名の一致率36%(14/39)38%(15/39)
媒体適合の一致率79%74%
拾う/没の一致率79%74%
取りこぼし率(クラウド拾い/ローカル没)6%(1/18)17%(3/18)
1コールの処理時間(中央値 / 最遅)36秒 / 66秒102秒 / 414秒
137本処理の所要時間(4並列時)約25分(実合計84分)約70分(実合計261分)

2. 全文判定に対する前置きフィルタ比較(res ≤ 100・98本)

短文コンテンツは本来クラウド側で1コール(全文読み)判定を行っているが、ここにローカル1段目を前置きして足切りを行った場合の精度である。

評価指標gemma4qwen3
型名の一致率48%43%
拾う/没の一致率80%72%
取りこぼし率16%(6/37)35%(13/37)

数値から見えた現実

  • 型名を当てさせるのは困難(36%): 19択の型分類をローカルモデルに正しく選ばせることは困難であった。ただし、両モデルがともに「採用」と判定したものに絞れば、型一致率は65%まで上昇した。
  • 門番(二値分類)としては機能する(79%): 採用か不採用かの二値判定であれば約8割一致し、取りこぼしも6%程度に収まる。
  • 速度と精度のバランス: gemma4 は qwen3 と同等の型一致率を示しながら、速度が約3倍速く、取りこぼし率は約3分の1であった。

最大の教訓:「自己一致」と「正しさ」を混同していた罠

本検証における最大の発見は、「15回判定して15回とも同じ結果が出る(安定している)」ことは、タスクが簡単であることを何一つ意味しない という点である。

15/15 という数字が示していたのは、単に「同一モデルが同一の入力に対して決定論的に同じ出力を繰り返している」という自己一致(再現性)に過ぎず、モデルの判断自体の正しさや課題の平易さを保証するものではなかった。

実際、クラウドと判定が食い違ったスレッドをローカルモデル(gemma4、温度0)で3回ずつ再判定させたところ、以下のような挙動を示した。

スレッドA(クラウド判定: 「どちらの媒体でもない」)
- ローカル試行1: 「叩き系」
- ローカル試行2: 「叩き系」
- ローカル試行3: 「叩き系」
→ 自己一致率: 100%(しかしクラウドの判断とは完全に食い違っている)

ローカルモデルは、間違った判断であっても高い再現性を持って同じ間違いを繰り返す。「出力が安定しているからローカルLLMでもこなせるはずだ」という前提の置き方自体が誤りであった。

実装・運用で踏んだ3つの落とし穴

ローカルLLMを実務パイプラインに組み込む過程で、以下の3つの技術的落とし穴に直面した。

1. format:“json” と思考(think)チャンネルの衝突

Ollama において、推論過程を出力するモデルで think: trueformat: "json" を同時に指定すると、モデルの思考チャンネル(thinking)側に回答が出力され、肝心の response フィールドが空文字列で返ってくる現象が多発した(12リクエスト中12件失敗)。

対処として、思考を有効にする際は JSON 制約オプションを外し、生成テキストの末尾から正規表現で JSON ブロックを抽出するパーサー(_extract_json)を経由させた。

2. 思考を切ると分類精度が壊滅する

処理速度を稼ぐために think: false を指定すると、1コールの処理時間は中央値3.6秒と劇的に高速化する。しかし、日本語の微妙なニュアンス判断ができなくなり、判定結果のほぼ全てが特定のスレッド型に偏るか、プロンプト内の説明文をそのまま鸚鵡返しする状態に陥った。複雑な文脈判定においては、思考ステップの省略は不可能であった。

3. num_ctx の既定値(4096)によるプロンプト頭切り

Ollama のデフォルトのコンテキスト長(num_ctx: 4096)は、実務の分類プロンプトに対して短すぎる。

1段目のプロンプトは型カタログや判定基準を含めて約4,700文字に達していたため、デフォルト値のままではプロンプトの先頭部分(型の定義一覧)が警告なしに切り捨てられ、モデルが存在しない架空の型名を捏造し始める原因となった。設定で num_ctx: 16384 を明示的に指定する必要があった。

# 専用インスタンス起動時のパラメータ設定
OLLAMA_NUM_PARALLEL=4 \
OLLAMA_HOST=127.0.0.1:11435 \
OLLAMA_KEEP_ALIVE=30m \
ollama serve

なぜ採用を見送ったのか

137本の全数測定とコスト試算を踏まえ、以下の理由から本線へのマージを見送り、コードを破棄(Revert)した。

  1. 削減効果とマシンスペック占有の不釣り合い: 安全な構成(res > 100 のみ)で得られるクラウド送信文字数の削減幅はわずか14.9%であった。このわずかな枠節約のために、日中作業マシンとして使用しているPCのGPUを毎日25分間フル稼働させ、ローカル画像生成等の業務とバッティングさせる運用コストは見合わない。
  2. 取りこぼしリスクの許容限界: 全件前置きフィルタを導入すれば送信文字数を52.5%削減できるが、採用すべき優良コンテンツの16%(本検証データでは6件)が未処理のまま取りこぼされる。現時点でコンテンツの在庫確保を犠牲にしてまでAPI枠を削る必然性はない。
  3. コードは残さず知見だけを固定する: 一度作成したコードを捨てるのは勇気がいるが、割に合わないと判明した複雑なフォールバック分岐を保守し続けることはリポジトリの負債になる。実装の履歴はGitログに残し、詳細な数値と教訓をドキュメントに記録して、本線のコードベースはクリーンな状態を維持した。

更新履歴

  • 2026-09-07: 初版起稿(検証PRおよび実測データに基づく)