🔬

第三幕:SLM パイプラインのその後 — モデル移行・並列度・パラメータの三重奏

に公開

この記事について

AI 対話ログと実験記録をもとに Claude Code が下書きを作成し、私が加筆修正しました。
本記事の内容は、個人開発環境(Mac 3台)で運用中の SLM パイプラインにおけるモデル選定・並列度チューニング・パラメータ設計の記録です。

前回の記事では、3台の Mac で SLM の再現性を確保した。ollama のバージョン統一と temperature=0 + seed=42 で出力は安定した。

今回は「再現性を確保した先」の話。パイプラインの品質とスループットを同時に改善しようとして、直感が通用しない場面に何度もぶつかった。


この記事で扱うトピック

再現性を確保したあとに待っていたのは、「パラメータを変えれば改善する」という直感が通用しない世界だった。

本記事では、SLM パイプラインの運用中に直面した5つのテーマを扱う。それぞれ独立した話題だが、ollama 0.17.7 へのアップデートを起点に連鎖的に発生した。

  1. コンテキスト長NUM_CTX=32768 を固定するに至った経緯
  2. モデル移行 — qwen2.5:14b → qwen3.5:9b、パラメータ数が少ないモデルが勝つ場面
  3. 並列度チューニング — concurrency=2 で逆に遅くなる現象と、2プロセス方式の実験
  4. 再現性の補足temperature=0 + seed=42 でも完全には揃わない(前回の続き)
  5. プロンプト改善の限界 — 8カテゴリの GT スコアと、改善が逆効果になるケース

はじめに

やること

  • ollama のバージョンアップを契機に NUM_CTX=32768 を固定した経緯を共有する
  • qwen2.5:14b → qwen3.5:9b のモデル移行で品質がどう変わったかを報告する
  • 分散実行の並列度チューニングで「遅くなる」現象を記録する
  • 8カテゴリの GT 照合スコアから、プロンプト改善の限界を示す

やらないこと

  • ollama や Metal API の内部実装の詳細解説
  • 他の推論エンジン(llama.cpp、vLLM 等)との比較
  • qwen 以外のモデルファミリーとのベンチマーク

NUM_CTX=32768 — コンテキスト長というボトルネック

なぜ 32768 なのか

当初、パイプラインでは num_ctx を明示的に指定していなかった。ollama のデフォルト(num_ctx=2048)でも、品質は安定していた。

ところが ollama を 0.17.7 にアップデートしたタイミングで、出力品質が不安定になった。原因を切り分ける中で、コンテキスト長の不足を疑った。AI 対話ログの1チャンクは数千〜10,000トークンになることがある。デフォルトの 2048 では入力が切り詰められ、情報が欠落している可能性があった。

NUM_CTX=32768 を明示的に固定したところ、品質が改善した。以来、この値を使い続けている。

TEMPERATURE=0
SEED=42
NUM_CTX=32768

この3つのパラメータはセットで意味を持つ。TEMPERATURE=0SEED=42 で再現性を確保し(前回の記事を参照)、NUM_CTX=32768 で入力の切り詰めを防ぐ。

NUM_CTX とメモリの関係

ここが重要な点だ。NUM_CTX=32768 を指定すると、ollama はその分の KV キャッシュをメモリに確保する。KV キャッシュはコンテキスト長 × 層数 × ヘッド数に比例して増えるため、長いコンテキストは推論速度とメモリ使用量の両方に影響する。

16GB マシン(wk-01)で qwen2.5:14b + NUM_CTX=32768 を動かすと、メモリの空きが35%程度まで減る。この「余裕のなさ」が、後述する並列度の問題に直結する。


qwen2.5:14b → qwen3.5:9b — 小さくて速いが品質は上

モデル差し替えという選択肢

パイプラインは qwen2.5:14b で構築した。品質は安定していたが、いくつかの問題を抱えていた。

  1. メモリ消費: 14B + NUM_CTX=32768 で 16GB マシンに余裕がない
  2. 推論速度: チャンクあたりの処理時間が分散実行のボトルネック
  3. 簡体字混入: 日本語出力に簡体字中国語が断続的に混ざる(P5 問題)

特に3つ目は厄介だった。日本語のサマリの中に突然「该」「是」「进行」が混ざる。プロンプトで「日本語で出力してください」と指定しても完全には防げなかった。

しかし、これまでモデルの差し替えは試したことがなかった。プロンプトやパイプラインの改善に注力していて、モデル自体を変えるという発想に至らなかった。

ollama 0.17.7 がきっかけ

前節で書いた ollama 0.17.7 へのアップデート。NUM_CTX の問題に対処する過程で、ふと気づいた。0.17.7 なら最新の qwen3.5:9b が利用できる環境が整っている。

せっかくなら、このタイミングでモデルの差し替えによる品質への影響を確認してみよう。パイプライン構築以来、初めてのモデル比較実験だった。

驚くべき結果

同一入力ファイル2本(c02, c04)を qwen2.5:14b と qwen3.5:9b の両方で処理し、GT 照合でスコアを比較した。

結果: 9B が 14B と同等以上。 パラメータ数が約4割少ないモデルが、品質で上回った。

ただし2ファイルの比較では不十分だ。eval-kit2 の全30ファイルで検証した。

品質評価には GT(Ground Truth)照合を使っている。各対話ログに対して「抽出されるべき情報」を事前に定義し、SLM の出力がそれをどれだけカバーしているかを測る方式だ。

用語 意味
GT(Ground Truth) Claude Code が対話ログを要約し、重要度付きで出力した「正解」候補。私が確認・同意したもの
Lv1 欠落すると要約として成立しない最重要情報
Lv2 あれば要約の質が上がる重要情報

Lv1-2 カバー率は、この両方を合わせた網羅率を示す。

指標 qwen2.5:14b qwen3.5:9b
Lv1 カバー率 88% 92%
Lv1-2 カバー率 80% 86%
簡体字混入 あり なし

P5(簡体字混入)が qwen3.5:9b では自然解消した。プロンプトの制約ではなく、モデル自体の多言語バランスが改善されたためだと推測している。

さらにプロンプト改善(v2: 設計・調査セッションへの抽出ヒント追加)を加えると:

指標 v1 v2
Lv1-2 カバー率 86% 91%(+5pt)
100%達成率 33% 53%(+20pt)

9B は 14B より約40%少ないメモリで動作し、推論も速い。チャンクサイズ 10KB でも Lv1-2 カバー率 100% を維持できる。

パラメータ数が少ない = 品質が低い、という直感は当てにならない。 モデルアーキテクチャの世代が新しければ、少ないパラメータで前世代の大きなモデルを超える。移行判断は実測で下すしかない。


並列度チューニング — 速くしたいのに遅くなる

構成のおさらい

3台の Mac で ollama を稼働させ、メッセージのキューイングで空いているワーカーに curl でリクエストを振り分ける分散実行方式だ。

マシン チップ RAM 役割
mbp M4 Pro 48GB ディスパッチャ + ノード
wk-01 M4 16GB ノード
wk-02 M4 24GB ノード

concurrency=2 の実験

「各ノードに2リクエスト同時に投げれば速くなるのでは?」

c03.02(11チャンク)で計測した。

設定 処理時間
全台 concurrency=1(1回目) 452秒
全台 concurrency=1(2回目) 413秒
mbp のみ concurrency=2 636秒

concurrency=2 で1.5倍遅くなった。

48GB の mbp でさえ、並列度を上げると悪化した。理由は ollama の内部動作にあると考えた。

遅くなった理由を考えてみた

Mac はユニファイドメモリなので、CPU/GPU 間のメモリ転送がボトルネックになることはない。48GB の mbp ならメモリ容量にも余裕がある。それでも遅くなった。

おそらく ollama は内部的に推論をシリアルに近い形で処理しているのではないか。Apple Silicon の GPU は推論を完全並列で実行するわけではなく、Metal のコマンドキュー上でシリアルに近い形で処理される可能性がある。2リクエストを同時に投げても、実際には1つずつ順番に処理され、キュー待ちとコンテキスト切り替えのオーバーヘッドだけが加算される。メモリがあっても、並列に推論が走るわけではないのだろう。

結果として、concurrency=2 は「2倍速」どころか「1.5倍遅」になった。

ollama 2プロセスという発想

「リクエストの並列度ではなく、ollama 自体を2プロセス起動すればどうか?」

mbp 上で ollama を2つのポートで起動し、4ノード構成を試した。

# デフォルト(ポート 11434)
ollama serve

# 2つ目(ポート 11437)
OLLAMA_HOST=127.0.0.1:11437 ollama serve

しかしこの方式もうまくいかなかった。プロセスは2つ起動でき、それぞれメモリも確保されたが、処理速度は変わらなかった。GPU の推論リソース自体が共有されている以上、プロセスを分けても意味がなかったのだろう。

最終設定

マシン RAM concurrency 理由
mbp 48GB 1 2 にすると遅くなる
wk-01 16GB 1 メモリ不足で 2 は不可
wk-02 24GB 1 2 で遅延傾向

全台 concurrency=1 が最速。 SLM の分散実行では、ノード数を増やす(水平スケール)方が、1ノードの並列度を上げる(垂直スケール)より効果的だった。


temperature=0 でも揺らぐ — 再現性の続き

前回temperature=0 + seed=42 による再現性確保を書いた。しかし並列度テストの副産物として、もう一つ分かったことがある。

11チャンクの同一入力を同一パラメータで2回実行した結果:

  • chunk2(@wk-01): 2回とも FAIL(GT Lv1-2 に欠落あり)
  • chunk7(@wk-01): 2回とも FAIL
  • 他のチャンク: 2回とも PASS(GT Lv1-2 を満たす)

FAIL するチャンクは固定されるが、出力テキスト自体は微妙に異なった。興味深いのは、ollama 0.13.5 の頃は同一条件で全く同じ結果が得られていたことだ。0.17.7 では微妙に異なる結果になる。

理由は正直わからない。一般的に GPU 推論では浮動小数点演算の順序がわずかに変わるだけで出力トークンが変わることがあるとされている。HW や OS、ollama のバージョン、モデルの組み合わせなどによる個体差のようなものがあるのかもしれない。

temperature=0 + seed=42 は「ほぼ同じ出力」を得るための実用的な設定であって、「完全に同じ出力」を保証するものではなさそうだ。前回の記事では「収束した」と書いたが、より正確には「実用上十分な範囲で収束した」だった。環境が変われば、この前提も変わりうる。


8カテゴリの品質スコア — パラメータの先にある壁

パラメータを固定し、モデルを選定し、並列度を調整した先に、プロンプトという壁がある。

8カテゴリの GT 照合スコア(qwen3.5:9b、全パラメータ最適化後):

カテゴリ Lv1-2 スコア プロンプト版
YATTAKOTO 96.6% v5
IDEA 93.3% v1
SHUSSHISHA 87.8% v1
KOKISHIN 74.1% v1
DECISION 71% v1(v2 は NG)
WAKATTAKOTO 70% v3
DATA 53% v4
YARUKOTO 39% v1

YATTAKOTO(96.6%)と YARUKOTO(39%)で 57pt の差がある。同じモデル、同じパラメータ、同じパイプラインで、抽出対象のカテゴリによってここまで差が出る。

DECISION では v2 プロンプトを試したが、特定ファイル(c01.07)で -16pt のデグレが発生し v1 にロールバックした。「改善しようとして悪化する」ことがある。プロンプト改善は単調増加ではない。


まとめ — SLM 運用で直感が外れる4つの場面

直感 実態
NUM_CTX を大きくすれば安心 HW のメモリの空き状態に依存する
パラメータ数が大きいモデルが強い 世代が新しければ少ないパラメータでも品質が上がる可能性がある
並列度を上げれば速くなる HW の構成や SW の実装に依存する
プロンプトを改善すれば品質が上がる カテゴリによっては逆効果

SLM をローカルで動かすのは簡単だ。ollama run で動く。しかし「測定しながら運用する」段階に入ると、パラメータ・モデル・並列度・プロンプトの4つが複雑に絡み合う。

「手軽に始められる」と「手軽に運用できる」の間には、深い溝がある。


上演目録(docs × AI による開発記録)

※本シリーズでは、試行錯誤のプロセスを「上演」に見立て、進行単位を「幕」として記述します。

第一部:docs × AI(全五章)

第二部:対話ログの整理(全六幕 + 幕間)

第三部:ワークフローとタスク管理(全四幕)

第四部:初めてのアプリを作る(全五幕)

第五部:SLM パイプラインのその後(幕数未定)


作成日: 2026-03-10
生成元: slm.2026-03-09.claude.{09,10,12,13,14}.md / slm.2026-03-10.claude.{01,14}.md(AI 対話サマリ)

記事作成プロセス

  • プロット作成: Claude Code(対話サマリから抽出)
  • 初期作成: Claude Code
  • 初回レビュー: 私
  • 添削: (未実施)
  • 投稿前レビュー: ChatGPT
  • 投稿後レビュー: NotebookLM

※ プロットは /devour-zenn-blog-plot スキルで生成し、/devour-zenn-blog-writing スキルで下書きを作成しました。

Discussion