專案實錄 · 12 分鐘閱讀

用 Python 部署本機大模型:Ollama、VRAM 預算與可用介面

本機模型是一個有記憶體預算、有延遲曲線的行程,不是黑盒。這篇實錄講清決定它能不能撐過一下午的幾件事:量化、VRAM 算術、一個 HTTP 介面、降級鏈,以及上線後該看的數字。

拉模型之前先看懂標籤

模型名後綴就是量化等級。q4_K_M 表示主權重 4 bit、敏感張量用 K 分組縮放,實際約每參數 4.5 bit。位數越低 VRAM 越省、精度略降,所以從 Q4_K_M 起步,只有自己的評測說不夠時才往上換。

params_b = 7
bits_per_weight = 4.5          # Q4_K_M,並不是整 4 bit
weights_gb = params_b * 1e9 * bits_per_weight / 8 / 1024**3
print(round(weights_gb, 2))

先算權重,再談效果

KV 快取也要算進去

權重只是帳單的一半。上下文裡每個 token 都要在每一層留一份 key 和 value 向量,8k 上下文往往又是 2 到 4 GB。權重加快取裝不下時,模型會溢到 CPU,吞吐直接掉一個數量級。

def kv_cache_gb(layers, hidden, ctx, bytes_per=2):
    # K 和 V 兩個張量,預設 fp16
    return 2 * layers * hidden * ctx * bytes_per / 1024**3

print(round(kv_cache_gb(32, 4096, 8192), 2))

上下文不是免費的

起服務,然後當普通介面呼叫

ollama serve 在 11434 埠暴露 HTTP 介面。向 /api/generate 發一個 stream 為 false 的阻塞 POST,回傳一個 JSON 物件。超時要設得比最慢的一次生成更長——預設那幾秒足夠把正常回答截斷。

import requests

payload = {
    "model": "qwen2.5:7b-instruct-q4_K_M",
    "prompt": "Say hi in five words.",
    "stream": False,
}
resp = requests.post("http://127.0.0.1:11434/api/generate", json=payload, timeout=120)
resp.raise_for_status()
print(resp.json()["response"])

一個介面,一個超時

有人在等的時候用流式

stream 為 true 時回應體是逐行 JSON:每行的 response 帶一個片段,最後一行的 done 為 true。按行解析、跳過空行、邊收邊列印,使用者不到一秒就能看到字,而不是盯著轉圈。

import json, requests

with requests.post(url, json=payload, stream=True, timeout=120) as resp:
    for line in resp.iter_lines():
        if not line:
            continue
        chunk = json.loads(line)
        print(chunk.get("response", ""), end="", flush=True)
        if chunk.get("done"):
            break

NDJSON,一行一個片段

降級到小模型,而不是拋 500

筆記本上 VRAM 溢出、守護行程報 500 都是常態。準備一條有序的模型鏈,逐級回退,並把「最後是誰答的」記下來,日誌才能在以後解釋品質為什麼掉了。答得慢總好過拋堆疊。

MODELS = ["qwen2.5:7b-instruct-q4_K_M", "qwen2.5:3b-instruct-q4_K_M"]

def generate(prompt):
    for name in MODELS:
        try:
            return name, call(name, prompt)
        except (TimeoutError, RuntimeError):
            continue
    raise RuntimeError("all local models failed")

可記錄的降級鏈

盯首字延遲、吞吐與失敗率

首字延遲是使用者的體感,每秒 token 數是硬體的產出。兩個都記,再按模型記失敗率;警示看中位數而不是平均數,否則一個 30 秒的離群值就能半夜叫人。

import statistics

first_token_ms = [820, 910, 780, 2400, 860]
print(round(statistics.median(first_token_ms)), max(first_token_ms))

看中位數,不看平均數

你的練習清單

常見問題

7B 模型 Q4 要多少VRAM?

權重約 4 GB,上下文快取按長度再加 1 到 4 GB。8 GB 卡比較從容;6 GB 要縮短上下文。

只有 CPU 能跑嗎?

能,每秒幾個 token。跑通宵批處理沒問題,對話體驗會很差。

用 Ollama 還是直接用 llama.cpp?

要模型管理和穩定 HTTP 介面就用 Ollama;要調採樣參數或跑特殊硬體再上 llama.cpp。

本機模型和雲上模型怎麼選?

資料不能出內網、或量大且品質夠用,選本機;要最強推理或彈性擴容,選雲上。

練一練模型監控

覆蓋漂移、延遲、小樣本評估與決策門檻值的題目。

打開練習

繼續閱讀: