JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

從提示到答案:大型語言模型實際上怎麼跑

每一句回覆都是一個詞元接一個詞元、分兩個階段生出來的。看懂預填充與解碼,就拿到了理解服務部署的鑰匙。

推論就是把模型往前跑一遍

訓練是用資料打造模型的昂貴一次性工程;推論(inference)則是你每次真正使用它時發生的事:你交給模型一段文字,它回給你更多文字。大型語言模型推論是日常的主力——同一份訓練好的權重會被往前跑上數十億次,世界上每一則對話、每一次自動完成、每一個 API 呼叫都是一次推論。

本質上模型只做一件小事:預測下一個詞元。在已知前文的情況下,它替每一個可能的下一個詞元算出機率,挑一個、接上去、再重複。一段 500 字的回答,大約就是 700 個這種步驟首尾相接而成。

p(x_t \mid x_{<t}) = \mathrm{softmax}(W\,h_t)

下一個詞元預測的核心:模型把隱藏狀態轉換成所有可能下一個詞元的機率分佈,再從中取樣一個。

第一階段:預填充一次讀完整段提示

當你的提示送進來時,模型會在一次平行運算中處理整段提示,這就是預填充(prefill)。因為提示的每個詞元一開始就都到齊了,GPU 可以把它們合成一個大型矩陣乘法一起算——極有效率,也正是 GPU 的強項。預填充與解碼之分是你最該內化的一個分界。

在預填充用一次並行前向處理之前,你的整段提示詞先被切分成詞元並轉成嵌入向量。

圖示:文字被切分為詞元 id,再映射為嵌入向量。

預填充是算力受限(compute-bound)的:面對 4,000 個提示詞元,GPU 忙著做真正的算術,核心全速運轉。預填充花的時間,就是使用者感受到的首詞元時間(time to first token,TTFT)——從按下 Enter 到看見第一個字出現之間的那段停頓。

第二階段:解碼一次寫一個詞元

提示消化完後,生成就開始了。解碼(decode)每跑一次前向就產出一個詞元:預測一個詞元、接上去、把略長一點的序列再餵回去、預測下一個。你無法跳著算——第 50 個詞元依賴第 49 個——所以解碼天生是循序的

解碼就是這個自迴歸迴圈:預測一個詞元,附加上去,再把略長一點的序列送回模型。

圖示:自迴歸的下一個詞元生成迴圈,每次前向附加一個詞元。

tokens = prefill(prompt)         # one big parallel pass over the prompt
while not finished:
    logits = model.step(tokens)  # one forward pass -> scores for next token
    nxt    = sample(logits)      # pick a token (greedy or sampling)
    tokens.append(nxt)           # grow the sequence by one, then repeat
預填充只發生一次;解碼迴圈則每產生一個詞元就跑一次。

解碼是記憶體受限(memory-bound)而非算力受限:每一步只碰一個新詞元,GPU 的運算單元大多在發呆,等著把模型權重從記憶體串流進來。這種不對稱——肥胖的平行預填充、瘦長的循序解碼——幾乎解釋了你日後會遇到的每一種服務最佳化。

使用者真正感受到的兩個數字

兩個指標描述了這段體驗。TTFT是出現任何東西前的等待(由預填充主導);每秒詞元數(tokens per second,TPS)是之後文字串流出來的速度(由解碼主導)。反應靈敏的助理要同時做到低 TTFT 與高 TPS;這是兩個不同的問題,要用兩種不同的辦法解。

為什麼這對後面一切都重要

記住一句話:預填充是算力受限且平行的;解碼是記憶體受限且循序的。KV 快取存在是為了讓解碼可以忍受,批次處理存在是為了在解碼期間填滿閒置的運算單元,而吞吐量與延遲的取捨全都活在這兩個階段之間的縫隙裡。下一篇我們就打開解碼會撞上的那道記憶體高牆。