上一篇我們提到,如果想抓出到底是誰偷走了 Tokens/s,就得先知道 LLM 到底在忙什麼。
所以今天先從最基本的問題開始:
LLM 到底是怎麼把一句話生出來的?
平常我們看到的可能是:
使用者:台灣最高的山是哪一座?
LLM:台灣最高的山是玉山。
看起來很簡單。
問題丟進去,答案跑出來。
但 LLM 實際上不是這樣工作的。
假設模型現在看到:
台灣最高的山是
它接下來要做的事情,其實只有一件:
猜下一個最有可能出現的 Token。
例如:
台灣最高的山是 → 玉山
產生「玉山」之後,事情還沒結束。
模型會把剛剛產生的內容接回去,再繼續猜下一個:
台灣最高的山是玉山 → 。
接著再猜下一個、再下一個。
所以你平常看到 ChatGPT 或其他 LLM 一個字一個字慢慢跑出來,其實不只是打字動畫。
背後真的正在不斷產生新的 Token。
Token → Token → Token → Token → ...
這也就是為什麼我們這個系列一直在講:
Tokens/s
簡單來說,就是模型每秒可以產生多少 Token。
這時候可能會有人問:
Token 不就是一個字嗎?
其實不是。
例如我們輸入:
今天的天氣很好
模型不會直接拿這串文字去運算。
在進入模型以前,還會先經過一個東西:
Tokenizer
它會把我們輸入的文字切成 Token,再轉成模型可以處理的數字。
概念上可以先想成:
今天的天氣很好 → Tokenizer → [1542, 921, 3841, ...]
可以把它想成去餐廳點餐。
你跟服務生說:
我要一份牛排,五分熟。
但廚房收到的可能不是完整的一句話,而是一組餐點代碼:
牛排,五分熟 → A17 + B05
LLM 其實也差不多。
我們看到的是文字,但模型真正處理的是一串 Token。
而且要注意:
1 個 Token 不一定等於 1 個中文字,也不一定等於 1 個英文單字。
不同模型使用的 Tokenizer 不同,切出來的結果也可能不一樣。
把剛剛講的東西串起來,其實可以先簡化成:
Prompt → Tokenizer → Tokens → LLM → 下一個 Token → 下一個 Token → 下一個 Token → ...
看到這裡,LLM Inference 最基本的輪廓其實就已經出來了。
但接下來才是真正有趣的地方。
因為模型「第一次把 Prompt 讀進去」,和後面「一個 Token、一個 Token 往外生成」,其實是兩種不太一樣的工作。
它們有自己的名字:
而之後我們會遇到的 KV Cache、Continuous Batching、PagedAttention,甚至 vLLM、SGLang 這些推論引擎的很多設計,其實都跟這兩個階段脫不了關係。
今天先記住一件事情就好:
LLM 不是一次把答案全部生出來,而是不斷預測下一個 Token。
而我們看到的 Tokens/s,就是這台「Token 生產機」到底跑得有多快。
下一篇我們就繼續往裡面拆:
Prefill 跟 Decode 到底差在哪?
也順便看看,第一批 Tokens/s 小偷是不是已經躲在裡面了。