上一篇 本地跑 Claude Code 實戰|Ollama + Gemma 4 + RTX 3070 使用心得 分享了用消費級顯卡跑 Claude Code 的心得,過程中我們對 Gemma 4 系列邊緣模型的效能和行為差異產生了一連串疑問——E2B 和 E4B 到底差多少?參數較小的 E2B 跑起來真的比較快嗎?為什麼有些任務 E2B 反而慢得離譜?這篇就把我們所有的 benchmark 數據和追查過程整理出來,從 TPS、首 token 延遲、回答品質一路測到最後找出 thinking 模式預設啟動的真相,順便把 thinking、temperature、top_k、top_p 這些常見參數的意義一次解釋清楚。
測試環境與方法
所有測試都在同一台機器上跑,硬體規格跟上一篇相同:Ryzen 5600X、RTX 3070 8GB VRAM、32GB DDR4-3200。Ollama 透過 REST API 對外提供服務,測試腳本用 Python 從另一台機器發送請求。
兩個被測模型是:
- gemma4:e2b — 總參數 5.1B,有效參數 2B,Q4_K_M 量化
- gemma4:e4b — 總參數 8.0B,有效參數 4B,Q4_K_M 量化(也就是
gemma4:latest,兩個 tag 的 SHA digest 完全一樣)
「E」代表 effective parameters(有效啟動參數),是 Gemma 4 家族專為邊緣裝置設計的變體。Ollama 顯示的「8.0B」是總參數,但實際推理時只會啟動其中的 4B。如果還不熟 Gemma 4 的家族成員可以參考 Google Gemma 4 模型介紹 。
呼叫 Ollama API 拿 TPS 數據的核心程式碼長這樣:
import requests
# 呼叫 Ollama 的 generate API
resp = requests.post('http://192.168.51.202:11434/api/generate', json={
'model': 'gemma4:e4b',
'prompt': 'Explain what Docker is in 100 words.',
'stream': False,
})
data = resp.json()
# 計算生成速度(token/秒)
eval_tps = data['eval_count'] / (data['eval_duration'] / 1e9)
print(f'生成速度:{eval_tps:.1f} t/s')
Ollama 回應裡的 eval_duration 是 nanoseconds,eval_count 是生成的 token 數,兩者相除就能得到生成階段的 token per second。
TPS 基準測試
先用 gemma4:e4b 跑三種長度的 prompt 看生成速度會不會隨輸出長度衰減:
| 測試 | 生成 tokens | Prompt 處理 (t/s) | 生成速度 (t/s) | 總耗時 |
|---|---|---|---|---|
| 短句 | 15 | 263.5 | 34.0 | 5.02s(含冷啟動 4.38s) |
| 中等 | 183 | 13.5 | 31.1 | 8.19s |
| 長文 | 2,652 | 662.5 | 29.7 | 91.25s |
幾個觀察:
- 生成速度穩定在 ~30 t/s,不會因為輸出變長而明顯衰減。
- 第一次載入模型要 4-5 秒(冷啟動),之後模型留在 VRAM,後續請求秒回。
- Prompt 處理在熱快取狀態下非常快(662 t/s),代表 prompt 不是瓶頸。
- 30 t/s 對一般對話算流暢,但跑 Claude Code 這類需要大量輸出的 agent 工作流會比較吃力。
Context length 對速度有影響嗎?
有人會擔心把 context length 調到 64K(VRAM 上限)會拖慢生成速度,所以實測了不同 num_ctx 設定下的表現:
| Context Length | 生成速度 (t/s) | Prompt 處理 (t/s) |
|---|---|---|
| 4,096 | 30.2 | 294.6 |
| 8,192 | 29.6 | 297.4 |
| 16,384 | 30.0 | 297.5 |
| 32,768 | 30.2 | 288.9 |
| 65,536 | 30.0 | 269.1 |
結論:降低 context length 對生成速度幾乎沒有影響,從 4K 到 64K 都穩定在 ~30 t/s。Prompt 處理在 64K 時稍微慢一點點(269 vs ~295),但差距很小。RTX 3070 跑 Gemma 4 E4B Q4_K_M 的瓶頸是 GPU 計算能力而非記憶體頻寬,短 prompt 下 KV cache 本來就很小,調低 context length 省不到什麼速度。
實用建議:直接把 context length 開到 VRAM 撐得住的最大值,不用擔心會變慢,反而能讓 Claude Code 這類吃 context 的應用更不容易爆。
首 Token 延遲(TTFT)
TPS 高不代表體感好。對 agent 工作流來說,從送出請求到收到第一個 token 的時間(Time To First Token, TTFT)更影響使用感受——每次呼叫工具都要等好幾秒才看到輸出,互動體驗會非常卡。
用 stream 模式測量 TTFT,每組測 3 次:
| 測試 | 平均 TTFT | 各次結果 |
|---|---|---|
| 短句 | 1,068ms | 2427ms(冷啟動), 391ms, 386ms |
| 中等 | 7,301ms | 9753ms, 10222ms, 1927ms |
| 長文 | 22,009ms | 23000ms, 23921ms, 19106ms |
短句排除冷啟動後約 390ms,反應算很快。但中等和長文的 TTFT 偏高(7-22 秒),這個現象一開始覺得很奇怪——prompt 處理速度明明很快,為什麼還要等這麼久才開始輸出?
後來發現答案藏在 Gemma 4 的 thinking 模式裡,這是這次測試最大的彩蛋,等等會詳細講。
E2B vs E4B 速度對比
直覺上,E2B 的有效參數只有 E4B 的一半,應該會比較快。實測結果:
| 指標 | gemma4:e2b | gemma4:e4b |
|---|---|---|
| 生成速度(短句) | 46.0 t/s | 33.6 t/s |
| 生成速度(中等) | 40.0 t/s | 30.5 t/s |
| 生成速度(長文) | 41.9 t/s | 30.2 t/s |
| TTFT(短句) | ~5,863ms | ~300ms |
| TTFT(中等) | ~6,338ms | ~7,501ms |
| TTFT(長文) | ~14,641ms | ~16,987ms |
結果很矛盾:E2B 純生成速度確實快了 35-40%(46 vs 34 t/s),但短句的 TTFT 反而比 E4B 慢將近 20 倍(5.8 秒 vs 0.3 秒)!這對「想用 E2B 跑互動式應用」的開發者來說是大警訊——數字上看起來快,實際用起來反而拖。
這個矛盾後面會解開,先看品質對比。
回答品質對比(難題)
用五道題目對比 E2B 和 E4B 在不同類型任務上的表現,涵蓋程式、邏輯、數學、摘要、創意寫作:
- 程式:寫 Python function 找 list 中第二大的 unique 值,處理 edge case
- 邏輯推理:根據五個人的身高關係(A>B、B>C、D>A、C>E)排序
- 數學運算:蘋果 3 顆 $2,買 10 顆多少錢?要列出計算過程
- 摘要理解:將一段 Git 介紹文字摘要成恰好 2 句話
- 創意寫作:寫一首關於凌晨 3 點 debug 的俳句
主觀評分(每題 10 分):
| 題目 | E2B | E4B | 說明 |
|---|---|---|---|
| 程式 | 8 | 8 | 兩者邏輯正確,都能處理 edge case,平手 |
| 邏輯推理 | 9 | 8 | 都答對 D>A>B>C>E,E2B 推導過程更清晰 |
| 數學運算 | 7 | 9 | 都算出 $6.67,E4B 多考慮整數組再算剩餘的購買邏輯 |
| 摘要理解 | 7 | 9 | E2B 漏掉 Linus Torvalds 和 Bitbucket,E4B 資訊更完整 |
| 創意寫作 | 7 | 9 | E4B 意象更生動(螢幕冷光、咖啡),E2B 偏抽象 |
| 總分 | 38/50 | 43/50 |
結論:E4B 在理解力、細節完整度、創意表達上明顯勝出。E2B 程式和邏輯不差,但摘要、創意這類需要更深理解的任務會丟細節。速度快 35-40% vs 品質差約 10%,看使用場景取捨。
反轉:小模型的真正用武之地
上面五道難題對 5B 級的小模型其實不太公平——這些都是雲端 70B+ 大模型的領域。換個角度,改測「輸入短、任務明確、輸出短」這類小模型擅長的工作,結果完全不一樣。
六道實用題目同時跑 E2B 和 E4B:
- 情感分類:判斷一段商品評論是正面、負面或中立
- 關鍵字抽取:從一段 Docker 介紹文字抽出 5 個關鍵字
- 結構化抽取:把一封 email 轉成 JSON(sender / date / subject / action_required)
- 指令生成:寫一個 shell 指令找出 /var/log 下 24 小時內修改過的 .log 檔
- 短句翻譯:把一段英文系統訊息翻成繁體中文
- Commit 訊息:根據變更描述寫一個 conventional commit message
測試結果:
| 任務 | E2B 耗時 / tokens | E4B 耗時 / tokens |
|---|---|---|
| 情感分類 | 15.4s / 165 | 13.9s / 2* |
| 關鍵字抽取 | 7.4s / 280 | 0.74s / 13 |
| 結構化抽取 | 7.9s / 299 | 2.28s / 63 |
| 指令生成 | 7.8s / 301 | 0.91s / 19 |
| 短句翻譯 | 14.9s / 588 | 1.10s / 23 |
| Commit 訊息 | 0.5s / 11 | 0.76s / 15 |
* 含冷啟動時間
看到這個表格時我們愣了一下:E4B 在這些任務上反而比 E2B 快 5-10 倍。明明 E4B 純生成速度比較慢(30 vs 40 t/s),怎麼會這樣?
關鍵在 token 數:E2B 每個任務都消耗了 165-588 個 tokens,但 E4B 只用 2-63 個。即使最終回答(response 欄位)很短,E2B 內部其實在做大量「看不見的生成」,而這正是 thinking 模式的證據。
品質方面,兩個模型在這些任務上的回答都很好。例如關鍵字抽取題:
- E2B:
Docker, containers, virtualization, software, packages - E4B:
Docker, virtualization, containers, OS-level, packages
E4B 多了「OS-level」更精準。Commit 訊息題:
- E2B:
feat: add exponential backoff retry to API client - E4B:
feat(api): add retry mechanism with exponential backoff for network failures
E4B 加了 scope (api) 更標準。對於這類明確任務,E4B 才是更好的選擇——速度快、品質好、而且不會多想。
參數解說:thinking、temperature、top_k、top_p
在進入 thinking 模式的真相之前,先把幾個常見的取樣參數解釋清楚。Ollama 對 Gemma 4 的預設參數是:
temperature 1
top_k 64
top_p 0.95
temperature(溫度):控制輸出的隨機性。0 = 永遠選機率最高的字(完全確定),1 = 按原始機率分布取樣,>1 = 更發散更有創意。寫程式、抽資料這類明確任務建議調低(0~0.3),創意寫作可以拉高(0.8~1.2)。top_k:每次生成 token 時只從機率最高的 k 個候選裡取樣,其他全部丟掉。值越小越保守、越收斂;越大越多樣。Gemma 4 預設 64 算偏寬鬆。top_p(nucleus sampling):累積機率達到 p 之前的所有候選都納入取樣池。例如top_p=0.95表示只取累積機率達 95% 的候選。跟top_k一起使用時,兩個會取交集。think:Ollama 在 chat models 上提供的開關,控制模型是否啟動「先推理再回答」的 thinking 模式。這個跟前面三個取樣參數無關,是 Gemma 4 系列特有的能力。
前三個取樣參數只影響「怎麼從候選 token 選下一個字」,不影響思考行為。真正讓 E2B 變慢的兇手是 thinking。
Thinking 模式的祕密
用 think 參數驗證
Ollama API 有一個 think 參數可以強制開關 thinking 模式:
resp = requests.post('http://192.168.51.202:11434/api/generate', json={
'model': 'gemma4:e2b',
'prompt': '...',
'stream': False,
'think': False, # 關閉 thinking
})
data = resp.json()
print(data['response']) # 最終答案
print(data['thinking']) # thinking 內容(think=True 時才有)
用「短句翻譯」這個 prompt 做對比實驗:
| 設定 | 耗時 | tokens | 說明 |
|---|---|---|---|
| E2B 預設(沒指定 think) | 20.0s | 583 | thinking 內容被 parser 過濾 |
E2B think=False | 0.73s | 18 | 跳過 thinking |
E2B think=True | 13.8s | 541 | thinking 內容寫入 thinking 欄位(1589 字元) |
| E4B 預設 | 6.35s | 20 | 沒有 thinking |
E4B think=True | 0.84s | 18 | 看起來不支援 thinking 模式 |
幾個關鍵發現:
- E2B 預設就是 thinking 開啟狀態,會多生 500 多個內部 tokens,但 Ollama 的
gemma4parser 把這些 thinking 內容過濾掉,只回傳乾淨答案。 - 把
think=False加到請求裡,E2B 速度提升 20 倍以上,跟 E4B 一樣快。 - 顯式設
think=True時,最終答案在response欄位、推理過程在thinking欄位(1589 字元的內部分析)。 - E4B 完全不支援 thinking 模式,不論
think設成什麼都一樣。
關閉 thinking 後品質會掉嗎?
速度提升 20 倍很誘人,但代價是什麼?用同樣 6 道實用題目對比 E2B 預設 vs think=False:
| 任務 | E2B 預設 | E2B think=False | 差異 |
|---|---|---|---|
| 情感分類 | NEGATIVE | NEGATIVE | 完全相同 |
| 關鍵字抽取 | Docker, containers, virtualization, software, packages | Docker, virtualization, containers, software, packages | 順序不同,內容一樣 |
| 結構化抽取 | “Submit your reports by Friday” | “submit your reports by Friday” | 大小寫不同 |
| 指令生成 | find ... -mtime -1 | find ... -mtime -1 | 完全相同 |
| 短句翻譯 | 請30分鐘後再試 | 請再試30分鐘後 | think=False 語序略不自然 |
| Commit 訊息 | feat: add exponential backoff retry… | feat: add retry mechanism with exponential backoff… | 兩者都好 |
結論:對於這類明確任務,關閉 thinking 對品質幾乎沒影響,但速度提升 10-20 倍。簡單任務用 think=False 是穩賺的設定。
當然,複雜任務(推理、創意寫作)關掉 thinking 才可能掉品質——這部分要看實際需求取捨。
為什麼 E2B 預設啟動思考?官方說法 vs 實測矛盾
到這裡有個疑問:為什麼 E2B 預設就會思考,E4B 不會?查官方文件後發現一個有趣的矛盾。
Google 官方 Thinking mode in Gemma 文件指出,Gemma 4 的 thinking mode 預設是關閉的,要在 system prompt 開頭加 <|think|> 控制 token 才會啟用。Ollama 官方 gemma4:e2b 頁面也說 thinking 預設關閉。
但實測顯示 E2B 在 Ollama 上的預設行為明顯有 thinking。官方說「opt-in」,實際卻是「預設開啟」。
用 raw=true 揪出兇手
要驗證到底是模型本身的行為還是 Ollama 在搞鬼,可以用 Ollama API 的 raw=true 模式——它會跳過 Ollama 內建的 chat template / renderer 處理,把 prompt 原樣送給模型。
手動構造兩種 chat template,依官方文件格式:
# 不含 think token
<|turn>user
[Prompt]<turn|>
<|turn>model
# 含 think token
<|turn>system
<|think|><turn|>
<|turn>user
[Prompt]<turn|>
<|turn>model
用相同的「短句翻譯」prompt 跑兩種 template,加上 Ollama 的預設行為做對比:
| 測試 | E2B tokens | E4B tokens |
|---|---|---|
Ollama 預設(raw=False) | 577(有 thinking) | 20(無 thinking) |
手動 no-think + raw=True | 18(無 thinking) | 19(無 thinking) |
手動 with-think + raw=True | 552(有 thinking) | 19(無 thinking) |
證據鏈:
- E2B「Ollama 預設 577 tokens」幾乎等於「手動 with-think 552 tokens」→ 證明 Ollama 預設給 E2B 的 prompt 裡有
<|think|>token。 - E4B 即使手動塞
<|think|>也不 thinking → E4B 訓練時沒被設定響應這個 token。 - E2B 用手動 no-think template 也只有 18 tokens,跟 API 加
think=False一樣快。
結論:是 Ollama 的關係。 Ollama 的 gemma4 renderer 預設會給 E2B 注入 <|think|> token,但對 E4B 不會。這跟 Ollama 官方頁面寫的「thinking 預設關閉」對 E4B 是事實,對 E2B 卻不是——看起來像是 Ollama 對 E2B 的特殊處理或 bug。
本地小模型最佳實踐
把這次測試的所有發現整理成可以直接拿來用的實踐建議:
- 需要 reasoning 用 E2B +
think=True:解題、推理、複雜分析的場景,讓 E2B 啟動 thinking 模式,雖然慢但答案更深入。 - 明確任務用 E4B 或 E2B +
think=False:分類、抽取、翻譯、commit 訊息等批次處理場景,跳過 thinking 速度快 10-20 倍,品質沒差。 - 跑 agent 工作流(Claude Code 等)用 E4B:E2B 預設啟動 thinking 會讓互動體驗變得超慢,E4B 直接出答案比較適合。
- Context length 直接拉滿:4K 跟 64K 速度差不多,但 64K 能避免 Claude Code 這類吃 context 的應用爆掉。
- 取樣參數預設就好:寫程式、抽資料這類需要穩定輸出的任務可以把
temperature調低到 0.1-0.3,創意寫作可以拉到 1.0 以上。
用 Python 呼叫的最佳實踐範例:
import requests
def quick_task(prompt: str) -> str:
"""跑明確任務時的最佳設定,速度最大化。"""
resp = requests.post('http://192.168.51.202:11434/api/generate', json={
'model': 'gemma4:e4b',
'prompt': prompt,
'stream': False,
'think': False, # 跳過 thinking
'options': {
'temperature': 0.2, # 降低隨機性
'num_ctx': 8192, # 不需要太大
},
})
return resp.json()['response'].strip()
# 用法:commit 訊息生成
msg = quick_task('Write a conventional commit message for: added retry logic to API client')
print(msg)
總結
這趟 benchmark 之旅原本只是想看看「RTX 3070 跑 Gemma 4 有多快」,最後卻意外挖出 Ollama 對 E2B 預設啟動 thinking 模式這個沒什麼人寫過的細節。重點整理:
- RTX 3070 跑 Gemma 4 E4B 約 30 t/s,E2B 約 40 t/s,速度跟 context length 無關。
- E4B 在難題上品質明顯勝出,但對「明確任務」兩者品質沒差。
- E2B 在 Ollama 上預設啟動 thinking,會多消耗 10-30 倍的 token,導致實際完成任務的速度反而比 E4B 慢 5-10 倍。
- 用
think=False可以關掉 thinking,速度提升 10-20 倍,對明確任務的品質幾乎沒影響。 - 追到 Ollama renderer 層級才確認,這個預設行為是 Ollama 對 E2B 的特殊處理,跟官方文件矛盾。
本地小模型不是萬能 AI,但用對了參數和場景,它在自動化工作流裡的價值非常高。如果還沒看過上一篇實戰心得,可以回頭看 本地跑 Claude Code 實戰|Ollama + Gemma 4 + RTX 3070 使用心得與踩坑筆記 。