TL; DR
- ローカルLLM(Deepseek R1 0528 Qwen3 8B)で
temperatureとseedが生成結果に与える影響を実験した seedを固定しないとtemperature = 0でも出力は再現されない(厳密に言えば量子化の結果として結果が揺れる可能性はある)。Geminiによると、GPU演算や並列処理の関係でも推論結果がズレることがあるとのこと。- コード生成(FizzBuzz)のように結果を収束させたい場合には、以下のようにすると良いと思われる。
temperature = 0+seed固定が最も安定temperatureを上げると、細部にバグが混入リスクが増える
- 俳句生成のような創作系タスクでは、以下のようにすると良いと思われる。
temperature = 0だと 中国語で推論され、日本語俳句の形式(5-7-5・季語)が崩壊しやすいtemperature = 0.3が最もバランスが良いと感じられた。それでもモデルの訓練データの関係から日本語俳句の形式は取られない。temperature = 1.2では出力が説明的、崩壊しがちになる。
- 推論(思考)に使われる言語が出力品質を大きく左右する
- 日本語プロンプトでも、日本語で推論されるとは限らない。
- 推論言語は訓練データの偏り+seed+temperatureの組み合わせで決まる。
repeat-penalty = 1.0では無限ループが発生することがある- 無限ループが発生した場合には、
repeat-penalty = 1.2程度まで上げると良い。
- 無限ループが発生した場合には、
- 結論としては、
temperatureだけを調整しても不十分で、seedと 推論言語を含めて設計しないと安定した品質は得られない。ローカルLLMのパラメータ調整は「おまじない」ではなく、モデル内部の挙動を理解した設計行為である。
きっかけ
前回の記事でローカルLLMのクラッシュと推論崩壊に苦しめられた。Context Lengthの設定を見直すとともに、「そもそもパラメータをどう設定すれば望ましい生成結果が得られるのか」という疑問が湧いた。
ここでは、top-pやContext Lengthといったパラメータよりも生成結果の変化に直結するtemperatureとseedに絞ってパラメータを動かし、それが生成結果にどのように影響を与えるのかを調べてみた。
実験環境
機材等
- MacBook M3 24GB RAM
- LM Studio バージョン0.4.2 (Build 2) / Metal llama.cpp v2.1.0
- モデル: Deepseek R1 0528 Qwen3 8B(GGUF、GPU Full Off-Load)
パラメータ
temperatureのみを変化させ、その他は以下の通り固定した。
| パラメータ | 値 |
|---|---|
| Context Length | 8192 |
| GPU Off-Load | 36(Full Off-Load) |
| repeat-penalty | 1.0 |
| top-p | 0.8 |
| seed | 42(固定) |
なお最初に気がついたこととして、 seedを固定しないとtemperature = 0でも結果が揺れる 。temperature = 0にも関わらず生成結果が揺れたのを見て、モデル内の確率分布が seed によって規定されるということを知った。
モデル内の確率分布を決定するのが seed であり、今回使用したモデルの初期値はランダム値であった。temperature はトークン間のスコア分布の大小を決めるものである。seed が固定され temperature = 0 の場合、複数回処理を繰り返しても、推論過程も含めて完全に同一の結果が得られる(量子化による微妙な差が生じる可能性はあるが、ほぼ無視できるレベルである)。
実験①: プログラムコードを生成する(FizzBuzz)
プロンプト
あなたは以下のMain.javaを読んでいます。
適切な形でfizzbuzz関数を実装してください。
不必要に思考せず、簡潔に答えてください。
class FizzBuzz {
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
fizzbuzz(i)
}
}
private void fizzbuzz(int i) {
// TODO: Implement here
// Spec:
// - This function print:
// "i: fizz" if i is multiple of 3
// "i: buzz" if i is multiple of 5
// "i: fizzbuzz" if i is multiple of both 3 and 5
// "i: -" if others
// - Each line should be ended with break line.
}
}
temperature = 0 (決定的)
3回とも完全に同一のコードが生成された。推論は英語で行われ、安定して正しい実装が行われた。
private static void fizzbuzz(int i) {
if (i % 15 == 0) {
System.out.println(i + ": fizzbuzz");
} else if (i % 3 == 0) {
System.out.println(i + ": fizz");
} else if (i % 5 == 0) {
System.out.println(i + ": buzz");
} else {
System.out.println(i + ": -");
}
}
temperature = 0.3 (やや多様)
バグが混入した。iを変数ではなく文字列リテラルとして出力してしまっている(1回目・3回目)。
// 1回目・3回目(バグあり)
private void fizzbuzz(int i) {
if (i % 15 == 0) {
System.out.println("i: fizzbuzz"); // ← iではなく"i"になっている
} else if (i % 3 == 0) {
System.out.println("i: fizz");
} ...
}
2回目はバグなしで temperature=0 の時と同じ実装が得られた。ロジック自体は3回とも同じで、temperature = 0.3ではトークン間の分布差が小さくなる為に、実装方針に大きな差は生まれなかったが、細部の精度が落ちる場合があることが分かった。
temperature=1.2(多様)
FizzBuzzの条件判定がif i % 15 == 0からif i % 3 == 0 && i % 5 == 0に変化するなど、より多様な実装が出た。引き続きバグを含む回答も混じる。
3回目には 推論過程が初めて日本語になった(1・2回目は英語)。推論/思考の言語もまた、トークン選択の確率によって決まる。また、今回は fizzbuzz() メソッドのみ出力し、 main() が出力されないケースも発生した。
// 3回目(temperature=1.2、推論が日本語になった回)
private static void fizzbuzz(int i) {
if (i % 15 == 0) {
System.out.println(i + ": fizzbuzz");
} else if (i % 3 == 0) {
System.out.println(i + ": fizz");
} else if (i % 5 == 0) {
System.out.println(i + ": buzz");
} else {
System.out.println(i + ": -");
}
}
なお、全ての試行を通じて、初心者がやりがちな「if i % 3 == 0を先に評価してしまうミス」は一度も発生しなかった。
コード生成の観察まとめ
temperatureを上げても収束型タスク(コード)では大きな傾向の変化はないが、細部にバグが混入するリスクがあるtemperature=1.2で、推論言語が英語→日本語に切り替わることがある- コードのように 正解を決めたい タスクには
temperature=0固定が安全
実験②: 俳句を生成する
以下のプロンプトを使った。
2月の季語を使いながら俳句を生成してください。
- 形式は5-7-5
- 冬の終わりの静寂さとの別れを彷彿させる俳句
- 破調(5-7-5の崩れ)も積極的に使用 // ← わざと揺れさせるように
- 対比する句を3句作成(例:朝・昼・夜) // ← わざと揺れさせるように
temperature = 0 (決定的)
推論が 中国語 で行われた。3回とも完全に同一の結果で、推論言語も変わらなかった。
(推論の一部)
嗯,用户想要用2月的季语创作俳句,主题是冬末的静寂感和离别氛围,
还特别要求使用破调和对毎句。这个需求挺有意思的,看来用户对俳句有一定了解...
出力された俳句(全3回共通):
句1: 朝の雪が舞い散る窓 静かに静かに静かだ 冬の終わりを告げる この季節の美しさは憂いを帯びて
句2: 午後の枯枝が揺れる風 冷たく冷たく冷たい 待ち続ける季語たちの ささやく別れの言葉
句3: 夜の灯りが揺れる窓 孤独に孤独に孤独だ 季節は去りゆくが 心はまだ冬の静寂に囚われて
俳句として成立していない(5-7-5どころか形式が別物)上、説明的かつ繰り返しが使われている。中国語で推論したことで、日本語俳句の形式制約が守られなかったと考えられる。
temperature = 0.3 (やや多様)
引き続き推論は中国語で行われた。ただし temperature=0 と同一の推論過程ではなく、出力にも多様性が出た。
1回目はプロンプトに答えていない句が出た。一部、回答に中国語文字(阴り)が含まれる。
句例1:
窓の外の雪が融け出す
- 季語: 雪が融け出す (雪解けの始まり)
- イメージ: 冬の終わり、雪が消え始める静寂と、その過程での期待や寂しさを表現。
句例2:
さくらんぼの花が咲き出す
- 季語: さくらんぼの花 (2月の梅の花、暁花)
- イメージ: 別れを彷彿させる、美しいが儚い花の開く様子。破調(5-7-5)ではありますが、季語と題意を満たしています。
句例3:
日は阴り少なくなった
- 季語: 日は阴り少なくなった (日陰が減った、日照時間が長くなった)
- イメージ: 冬の厳しさが和らぎ、春に向かう静かな変化。別れの訪れを暗示。
2回目は1回目と大きく変わり、異なる季語・構成になった(コード実験と比べると、自然言語の方がtemperatureの変化に対して出力の揺れが大きい)。
- 季語:立春
- 句: 立春の木枯らぎ / さよなら雪 / 窓の外で待ち
- 説明: 「立春」が季語、「木枯らぎ」(枯れ木)が2月の風景を表す。破調の6-7-5で始まり、冬の終わりの静寂と去りゆく雪、窓の外で待ち続ける情景を描く。
- 季語:梅雨
- 句: 梅雨の朝露 / かすかに春の匂い / うっかり吸い込む
- 説明: 「梅雨」が季語、「朝露」が2月の季語風景。破調の5-7-5で、ほのかな春の匂いを帯びた湿った空気を、つい意識せずに吸い込む無邪気さを表現。
- 季語:雫
- 句: 雫の花蕾 / 未だ届けられぬ手紙 / 春待ちの季語
- 説明: 「雫」(しぶき、滴)が季語、「花蕾」(芽)が春の訪れを象徴。破調の5-5-5で、まだ届いていない別れの手紙と、待ち続ける季語の重ね合わせを表現。
3回目は repeat-penalty = 1.0の影響で無限ループに陥り、俳句を最後まで生成できなかった 。
temperature = 1.2 (多様)
1回目は、俳句として成立していないものの説明的な言葉は控えられている点に注目した。中国語で推論されていた。
- 朝霜降る 春待ちかな
- 雪降り 山の間は 静かに
- 夜の季語に 山は青し
2回目は 日本語で推論 した。temperatureの値により、推論/思考に使う言語として日本語が選択されたと考えられる。ただし、俳句としては成立していない。
(2回目の推論)
まず、ユーザーのクエリを理解します。ユーザーは2月の季語を使って俳句を作成してほしいと要請しています。
- 二月寒風、枯れ木そよぐ静かに、別れの訪れ
- 梅雨明けの青空、雪の跡が消え行く、心のほろび
- 古池の水面結晶、冬が終りを告げる、孤独な夜
- 6-7-5の句:風雪舞う空庭、往く者もなしの別れ、季節の幕下り
- 7-5-5の句:灯す街路灯一筋、静寂が二重に深まる、時の流れを想う
- 5-8-5の句:窓の外白い霧、待ち続ける影たちが重なる、冬の最後の夢
3回目は、やや説明的な言葉が目立つ。こちらも中国語で推論/思考していた。
1. 朝: 正月に雪かすがり、君はもう去りぬ
2. 昼: 窓辺、霜枯らむ、そよ風立ち込める
3. 夜: 長閑、月光の下、さよならの言葉響く
俳句生成のまとめ
temperature = 0では、Deepseekは中国語で推論し、日本語俳句の形式制約が守られない。トレーニングのほとんどが中国語によって行われていることに起因すると考えられる。temperature = 0.3では、推論過程の変化が生まれ、出力の質も変わる。個人的には最もバランスの良い生成をしていると感じた。temperature = 1.2では、思考の幅が広がってしまい、俳句として成立しなくなり、また説明的な言葉が目立つようになる。一言で言えば回答が崩壊する。repeat-penalty=1.0だと、無限ループリスクがある。
加えて、見落としがちな変数としての推論言語
最も印象的だったのは、 推論(思考)に用いられる言語が出力の質を大きく左右する という点だ。
Deepseekは大量の中国語・英語のデータで訓練されていると想定する。seed 固定、temperature = 0 という決定的な状態では、推論言語として中国を選択しやすくなるのはその為であると考える。中国語で俳句の作り方を「考える」と、古典詩の知識がベースになり、日本語の俳句の5-7-5・季語という制約へのアクセスが弱くなる。
一方、プログラムコードは、プログラム作成用途で英語で訓練されたモデルを使うと良い。これは、プログラムコードの訓練データ(ドキュメント、マニュアル、エラーログなど)が英語圏に集中していることと対応している。
また、「日本語でプロンプトを書いたから日本語で推論される」わけではない。推論言語はモデルの訓練データの偏りと、 seed 、 temperature の組み合わせによって決まる。
パラメータ設定の指針
今回の実験から導いた実践的な指針をまとめる。
結果を収束させたい場合(プログラミング)
- 英語圏で、プログラミング用途に訓練されたモデルを選ぶ
seedを固定するtemperatureは0に設定する
多様性を出したい場合(創作系タスク)
- そのタスクの言語に強いモデルを選ぶ(日本語の俳句なら日本語で訓練されているモデル)
seedは固定するtemperatureは0.3程度にする。1.2まで広げてしまうと回答品質は落ちるように見える。repeat-penaltyは1.2程度に設定する(無限ループ防止)- 応答長に上限を設けて完走不可リスクを軽減する
まとめ
temperature だけで生成結果を制御しようとするのは難しい。seed も一緒に固定しなければ、同じ temperature = 0 でも実行ごとに結果がばらつく。また、推論に使われる思考言語という変数が、特に自然言語生成の品質に大きく影響することが分かった。
とはいえ、temperature = 0が「常に最良」というわけでもない。コードのような収束型タスクには適しているが、創作系では推論言語が適切でなければ形式制約を無視した出力が返ってくる。
ローカルLLMで品質の高い出力を安定して得るには、モデルの訓練言語・タスクの特性・パラメータの組み合わせを一体として設計する必要がある。パラメータチューニングは「おまじない」ではなく、モデルの内部動作への理解を伴って初めて機能するものだと感じた。
今後の課題
- 日本語に特化したモデルを使い、本当に俳句をきちんと作り上げることができるのかを検証する。
- Deepseekで検討した傾向と同様の結果になるのかを確認する。