tk3.biz
ブログ一覧に戻る Back to Blog

thinking を切っていいのか測ったら、採点器のほうが2回壊れていた I Measured Whether Turning Off Thinking Was OK, and It Was My Grader That Broke Twice

はじめに

CLI を整えた回で、 thinking を既定でオフにしました。理由はこう書いています。

これがトークンを大量に食います。 「日本語で1文だけ自己紹介してください」という質問に対して、思考に 1313 文字使っていました。

速度しか見ていません。 品質が落ちていないかは測っていませんでした。 切ったせいで損をしている可能性が残ったままです。

測りました。結論は「この難易度帯では切って問題なし」でしたが、 そこに至るまでに採点スクリプトのバグを 2 回踏んでいます。 今回の収穫はそちらでした。

測り方を決める

品質を主観で採点すると比較になりません。 なので正解が機械的に検証できる課題だけを使うことにしました。

「説明文が丁寧になるか」は測れないので、測らないと決めました。 測れないものを測ったふりをするほうが害が大きいと考えています。

課題は 5 問。thinking が効きそうなものと、効かないはずのものを混ぜてあります。

課題種類想定正解
T1多段の算術(まとめ買い割引)効きそう1800 円
T2数え上げ(包除原理)効きそう53 個
T3日付計算(月跨ぎ)効きそう2026-11-10
T4単純な抽出(2 番目に大きい値)効かないはず7
T5単位変換(秒 → 時分秒)効かないはず2 時間 46 分 40 秒

正解はすべて計算で検算しました。

「効かないはず」を混ぜたのが肝です。 もし全課題で差が出たら、それは thinking の効果ではなく 別の要因(温度、運の偏り)を疑うべき、という切り分けになります。

前回で 「もっともらしい説明ほど危ない」を学んだので、 仮説が外れる可能性を先に組み込んでおきました。

条件は temperature 0、max_tokens 4096、各条件 2 回ずつです。

1 回目: thinking OFF のほうが正答率が高い?

結果はこうでした。

ON   正答 4/5
OFF  正答 5/5

T2(数え上げ)で thinking ON だけが不正解。 4171 文字も考えて間違えている。

「考えすぎて自滅した」という筋書きが浮かびました。面白い結果です。

念のため、実際の出力を見ました。

3または5で割り切れる数は 33 + 20 - 6 = 47個です。
したがって、3でも5でも割り切れない数は 100 - 47 = 53個となります。

答え: 53個

正解していました。 包除原理を正しく使っています。 不正解だったのは私の採点でした。

バグ 1: 採点対象が条件によって変わっていた

本文全体に正規表現をかけていました。

thinking を出すと、本文に途中計算が並びます。33 個、20 個、6 個、47 個……。 一方 thinking OFF は結論だけを短く返します。

同じ正規表現を当てても、見ている文章の量がまるで違うわけです。 ON 側だけが余計な数字に晒される、フェアでない採点でした。

「答え:」で始まる行だけを採点するよう直しました。 出力形式を指示してあるので、形式を守っているかも同時に測れるという副次効果もあります。

2 回目: 今度は両方とも不正解

直して測り直したら、T2 が両条件とも不正解になりました。

前回 OFF は正解していたのに、です。明らかにおかしい。

バグ 2: \b が日本語で効かない

正規表現に \b53\b を使っていました。

\bが効かない理由。答え: 53個 という文字列で、3と個がどちらも単語文字なので、その間に単語の切れ目が立たない

\b は「単語文字と非単語文字の境目」にマッチします。 そして .NET の正規表現は、漢字やかなを「単語文字」として扱います。

「53個」の 3 と 個 はどちらも単語文字なので、その間に境界が立ちません。

"答え: 53個" -match '\b53\b'          # False
"答え: 53個" -match '(?<!\d)53(?!\d)'  # True

正解を不正解と記録していたわけです。

数字を囲みたいなら、\b ではなく 「前後が数字でないこと」を直接書くのが確実でした。

今度は採点器を先に検証した

3 度目は、測る前に採点器のほうをテストしました。

正解を拾えるか。

答え: 1800円        -> True
答え: 1,800円       -> True
答え: 53個          -> True
答え: **7**         -> True
答え: 2時間46分40秒 -> True

誤答を弾けるか。

答え: 47個    -> False
答え: 9       -> False
答え: 1700円  -> False

両方を実際の文字列で確かめてから、本番を回しました。

確定した結果

正答率は同じで、時間とトークンだけが3〜4倍になる。正答率は両方10/10、平均所要時間はON 39.5秒に対しOFF 12.6秒、平均生成トークンはON 925に対しOFF 238

正答平均時間平均トークン思考
thinking ON10/1039.5 秒9251,931 字
thinking OFF10/1012.6 秒2380

正答率は同じ。時間が 3.1 倍、トークンが 3.9 倍。

課題別に見ても、時間差は一貫していました。

課題種類ONOFF時間比
T1多段の算術26.6s / 508tok9.0s / 145tok3.0 倍
T2数え上げ74.4s / 1762tok21.8s / 454tok3.4 倍
T3日付計算44.7s / 1028tok13.1s / 249tok3.4 倍
T4単純な抽出19.0s / 444tok5.6s / 78tok3.4 倍
T5単位変換34.9s / 864tok13.4s / 261tok2.6 倍

効きそうだと踏んだ T1〜T3 でも、正答率は上がりませんでした。 どちらも全問正解です。

T4 が象徴的でした。「配列 [3, 7, 2, 9, 4] の 2 番目に大きい値」を答えるだけで、 969 文字考えて 3.4 倍の時間をかけています。答えは同じ 7 です。

この結論が言えること、言えないこと

言えること。 CLI で thinking を切った判断は、この難易度帯では正しかった。 対価を払うだけの効果はありませんでした。

言えないこと。 今回の課題は「正解が一意に決まり、手順も定型」のものばかりです。 もっと難しい問題では差が出る可能性があります。 その領域は測っていません。

llm -Think "..." で個別に有効化できるようにしてあるので、 「難しそうなときだけ付ける」という使い分けは残してあります。

教訓: 測定器を先に疑う

前回のベンチで「差を見つけたら、まず測定系のばらつきを確かめる」と書きました。 その直後に、今度は測定器そのもののバグを 2 回踏んでいます。

共通していたのは、**最初の結果が「面白かった」**ことです。

  • 1 回目は「thinking は考えすぎて自滅する」
  • 2 回目は「両方間違えるほど難しい問題だった」

どちらも記事になりそうな筋書きで、そのぶん疑いにくかった。 助かったのは、どちらも物理的・論理的におかしい点があったからでした。 包除原理を正しく解いている出力を見れば、不正解のはずがありません。

測定結果より先に、測定器が正しいかを確かめる。 そのコストは、今回で言えば数分でした。 3 回測り直した時間のほうがずっと長くついています。

まとめ

  • thinking を切った判断は、この難易度帯では正しかった(正答率は同率)
  • ただし 3.1 倍の時間と 3.9 倍のトークンを使う。定型課題では払う意味が薄い
  • 難しい問題での差は測っていない。切り替えられる余地は残す
  • \b は日本語で効かない。 .NET は漢字・かなを単語文字とみなす
  • 採点器は、本番の前にテストする。 正解を拾えるかと、誤答を弾けるかの両方

測りたかったのは thinking の効果でしたが、 いちばん学んだのは自分の採点スクリプトの信用度でした。

Introduction

In the post where I set up the CLI, I turned thinking off by default. Here’s the reason I gave:

This eats a lot of tokens. For the question “Introduce yourself in one sentence in Japanese”, it used 1313 characters on thinking.

I only looked at speed. I never measured whether quality dropped. The possibility that turning it off was costing me something was still open.

So I measured it. The conclusion was “at this difficulty level, turning it off is fine”, but on the way there I hit bugs in my grading script twice. That turned out to be the real takeaway.

Deciding How to Measure

If I graded quality by gut feeling, there’d be nothing to compare. So I decided to use only tasks whose answers can be checked mechanically.

I can’t measure “does the explanation get more careful”, so I decided not to measure it. I think pretending to measure something you can’t measure does more harm.

There are 5 tasks. I mixed tasks where thinking should help with ones where it shouldn’t.

TaskTypeExpectationAnswer
T1Multi-step arithmetic (bulk discount)Should help1800 yen
T2Counting (inclusion–exclusion)Should help53
T3Date arithmetic (across months)Should help2026-11-10
T4Simple extraction (second-largest value)Shouldn’t help7
T5Unit conversion (seconds → h/m/s)Shouldn’t help2 h 46 min 40 s

I verified every answer by calculation.

Mixing in the “shouldn’t help” tasks is the key. If a difference showed up on every task, that would point not to thinking but to some other factor (temperature, a run of luck), which gives me a way to separate the two.

In the previous post I learned that “the more plausible the explanation, the more dangerous it is”, so I built in the possibility of my hypothesis being wrong from the start.

Conditions: temperature 0, max_tokens 4096, two runs per condition.

Round 1: Thinking OFF Is More Accurate?

Here’s what I got:

ON   正答 4/5
OFF  正答 5/5

On T2 (counting), only thinking ON was wrong. It thought for a whole 4171 characters and still got it wrong.

A storyline came to mind: “it overthought and tripped itself up”. An interesting result.

Just in case, I looked at the actual output:

3または5で割り切れる数は 33 + 20 - 6 = 47個です。
したがって、3でも5でも割り切れない数は 100 - 47 = 53個となります。

答え: 53個

It was correct. It used inclusion–exclusion properly. What was wrong was my grading.

Bug 1: What Got Graded Depended on the Condition

I was running a regular expression over the whole response.

With thinking on, the response lists the intermediate steps: 33, 20, 6, 47…. With thinking off, it returns just a short conclusion.

The same regex was looking at completely different amounts of text. Only the ON side was exposed to extra numbers, so the grading wasn’t fair.

I changed it to grade only the line starting with “答え:” (Answer:). Since I tell the model the output format, this has the side benefit of also measuring whether it follows the format.

Round 2: Now Both Are Wrong

After the fix I measured again, and T2 was wrong under both conditions.

Even though OFF had been right the previous time. Clearly something was off.

Bug 2: \b Doesn’t Work with Japanese

My regex used \b53\b.

Why \b doesn't work. In the string 答え: 53個 ("Answer: 53 items"), 3 and 個 are both word characters, so no word boundary falls between them

\b matches “the border between a word character and a non-word character”. And .NET regular expressions treat kanji and kana as “word characters”.

In “53個”, the 3 and the 個 are both word characters, so there’s no boundary between them.

"答え: 53個" -match '\b53\b'          # False
"答え: 53個" -match '(?<!\d)53(?!\d)'  # True

So I had been recording correct answers as wrong.

If you want to fence off a number, instead of \b, writing “not a digit before or after” directly is the reliable way.

This Time I Tested the Grader First

On the third round, before measuring, I tested the grader itself.

Does it pick up correct answers?

答え: 1800円        -> True
答え: 1,800円       -> True
答え: 53個          -> True
答え: **7**         -> True
答え: 2時間46分40秒 -> True

Does it reject wrong ones?

答え: 47個    -> False
答え: 9       -> False
答え: 1700円  -> False

Only after checking both against real strings did I run the actual measurement.

The Final Results

Accuracy is the same; only time and tokens grow 3–4×. Accuracy is 10/10 for both, average time is 39.5 s for ON versus 12.6 s for OFF, and average generated tokens are 925 for ON versus 238 for OFF

CorrectAvg timeAvg tokensThinking
thinking ON10/1039.5 s9251,931 chars
thinking OFF10/1012.6 s2380

Same accuracy. 3.1× the time, 3.9× the tokens.

Broken down by task, the time difference was consistent.

TaskTypeONOFFTime ratio
T1Multi-step arithmetic26.6s / 508tok9.0s / 145tok3.0×
T2Counting74.4s / 1762tok21.8s / 454tok3.4×
T3Date arithmetic44.7s / 1028tok13.1s / 249tok3.4×
T4Simple extraction19.0s / 444tok5.6s / 78tok3.4×
T5Unit conversion34.9s / 864tok13.4s / 261tok2.6×

Even on T1–T3, where I expected thinking to help, accuracy didn’t go up. Both got every question right.

T4 says it all. Just to answer “the second-largest value in the array [3, 7, 2, 9, 4]”, it thought for 969 characters and took 3.4× as long. The answer was the same 7.

What This Conclusion Does and Doesn’t Show

What it shows: turning thinking off in the CLI was the right call at this difficulty level. The benefit wasn’t worth the cost.

What it doesn’t show: every task this time had “a single correct answer and a routine procedure”. Harder problems might show a difference. I haven’t measured that range.

I made it possible to turn it on per request with llm -Think "...", so I can still “add it only when something looks hard”.

Lesson: Suspect the Measuring Instrument First

In the previous benchmark post I wrote “when you find a difference, first check the variance of your measurement setup”. Right after that, I hit bugs in the measuring instrument itself, twice.

What the two had in common was that the first result was “interesting”.

  • The first time: “thinking overthinks and trips itself up”
  • The second time: “the problem was so hard that both got it wrong”

Both were storylines that could make a post, and that made them harder to doubt. What saved me was that both had something physically or logically wrong with them. An output that solves inclusion–exclusion correctly can’t be a wrong answer.

Before trusting the measurement, check that the measuring instrument is right. This time, that cost a few minutes. Measuring three times over cost far more.

Summary

  • Turning thinking off was the right call at this difficulty level (same accuracy)
  • But thinking uses 3.1× the time and 3.9× the tokens. For routine tasks, it’s hardly worth paying for
  • I haven’t measured the difference on hard problems. I keep the option to switch it on
  • \b doesn’t work with Japanese. .NET treats kanji and kana as word characters
  • Test the grader before the real run. Both whether it picks up correct answers and whether it rejects wrong ones

What I wanted to measure was the effect of thinking, but what I learned most about was how far I could trust my own grading script.

関連記事 Related Posts

ローカルLLMをターミナルから日常的に使えるようにした Making a Local LLM Part of My Daily Terminal Use

速くしただけでは使わない。PC起動後に`llm`と打つだけで答えが返る状態にするまでと、アイドル時に22.7GBを手放す仕組み、そして48GBを無駄に占有していたバグの話です。 Making it fast wasn't enough to get me to use it. How I got to the point where typing `llm` after booting the PC returns an answer, a way to give back 22.7GB when idle, and a bug that was needlessly taking up 48GB.

自分が書いた1418行を3つのAIにレビューさせたら、実バグが11件出た I Had Three AIs Review 1418 Lines I Wrote, and They Found 11 Real Bugs

Claude・Codex・ローカルQwenの3枚に、自分で書いて自分で動かしていたスクリプトをレビューさせました。3件は自力では気づけないもので、1件は「レビュー結果を集計するスクリプト自体」のバグでした。一方で、3枚とも見逃した欠陥もあります。 I had three reviewers, Claude, Codex and a local Qwen, review scripts I had written and been running myself. Three were bugs I couldn't have found on my own, and one was in the very script that tallies the review results. There was also a defect all three missed.

使っていない画像機能を外したら、メモリは1GiB減り、速度は変わらなかった Dropping an Unused Image Feature Saved 1 GiB of Memory and Didn't Change Speed

ローカルLLMのサーバが、テキストしか使わないのに画像用のmmprojを毎回読み込んでいました。外すと速くなるかを、前回まとめたチェックリストに沿って測りました。1回目は計測中のずれが大きく速度について何も言えず、裏で別のAIセッションが動いていたと分かったので、止めて測り直しました。メモリは1GiB減り、速度は3%未満の差で変わりませんでした。既定は外すことにしました。 My local LLM server was loading the image mmproj on every start, even though I only use text. I measured whether dropping it makes things faster, following the checklist from my previous post. In the first run the drift during measurement was too large to say anything about speed, and it turned out another AI session was running in the background, so I stopped it and measured again. Memory went down by 1 GiB, and speed stayed the same, within 3%. I made dropping it the default.