跳至主要内容

Scoring API

Scoring 呼叫執行 forward 計算,但不進入自迴歸取樣迴圈。它們是評估、排序、不確定性估計,以及任何需要取得 logits 而不需要 decode loop 的工作負載之 C++ 進入點。

函式用途
score在固定 loop 深度下取得完整的 [seq_len, vocab] logits。
score_last_token僅輸出最後一個位置的 logits [vocab] — 最低記憶體流量。
score_logit_lens每個 loop 深度的最終 logits [n_loops, vocab],用於深度演化分析。
score_with_uncertainty兩個深度的 forward,附帶每 token 的 KL 不確定性訊號。
score_with_consistency_escalation自適應深度 forward:當低深度與高深度不一致時提升 loop 次數。

除非另有說明,本節中每一個 scoring 函式:

  • 定義於 include/tinyloop.hnamespace tinyloop 下。
  • 會修改 Model 內部工作 buffer(buffers.mainbuffers.norm、scratch);若無外部序列化,對同一個 Model* 的並行呼叫是不安全的。
  • 不會推進任何 decode cache。除非 API 明確使用 cache helpers,否則不會配置或附加到 RuntimeKVCache
  • 在違反不變條件時(null 指標、維度越界、GPU OOM)會向 stderr 寫出診斷並中止。呼叫端應將這些視為致命錯誤:runtime 不會 unwind。
  • 結果寫入由呼叫端配置的輸出 buffer;logits 本身不會進行 heap 配置。

score

void score(Model* model,
const int32_t* tokens,
int seq_len,
float* logits_out,
int n_loops = 8);

在固定 loop 深度下,為給定的 prompt 計算完整的 [seq_len, vocab_size] logit 張量。

參數

名稱型別預設描述
modelModel*已載入的模型指標。不得為 nullptr
tokensconst int32_t*Token-id 陣列,長度為 seq_len。Token id 必須滿足 0 <= t < vocab_size
seq_lenint要評分的 token 數量。必須滿足 1 <= seq_len <= max_seq_len
logits_outfloat*輸出 buffer,連續記憶體,大小為 seq_len * vocab_size * sizeof(float)。呼叫端配置;無需預先歸零。
n_loopsint8共享 loop block 的執行次數。必須滿足 1 <= n_loops <= 64。較高的值以線性計算代價換取品質提升(至飽和為止)。

回傳

void。結果以 row-major 順序寫入 logits_out:元素 (t, v) 位於偏移量 t * vocab_size + v

例外 / 錯誤條件

Runtime 會向 stderr 寫出訊息並在以下情況中止:

  • model == nullptr
  • seq_len < 1seq_len > max_seq_len(於 load_model 時指定)。
  • n_loops < 1n_loops > 64
  • Prefill 期間或內部 scratch 輸出 buffer 配置時發生 GPU 記憶體不足。

備註

  • 記憶體佔用。 在所有 scoring 變體中,score 的輸出佔用最大。當 vocab_size = 50257seq_len = 2048 時,光輸出張量在 FP32 下就約 393 MB。在 decode 熱路徑請優先使用 score_last_token
  • 計算為主導成本。 對於典型形狀(d = 2048L = 8seq_len = 1024),loop-block GEMM 佔 wall-clock 的 90 % 以上;head projection 是次要成本。
  • prefill_chunk 互動。 當模型以 prefill_chunk > 0 載入時,score 受 chunk 列預算約束:seq_len 超過 chunk 大小的呼叫將以多個 pass 執行,可降低尖峰 VRAM,但換來 kernel launch overhead。

範例

std::vector<int32_t> tokens = tokenize("The quick brown fox");
std::vector<float> logits(tokens.size() * vocab_size);
tinyloop::score(model, tokens.data(), tokens.size(), logits.data(), /*n_loops=*/8);
// logits[(t * vocab_size) + v] 是位置 t 上 token v 的未歸一化 logit

另見

score_last_token

void score_last_token(Model* model,
const int32_t* tokens,
int seq_len,
float* logits_out,
int n_loops = 8);

僅在最後一個 token 位置計算 logits。適用於排序、為候選延續評分,以及每位置 logits 會被浪費的 decode helper。

參數

名稱型別預設描述
modelModel*已載入的模型指標。不得為 nullptr
tokensconst int32_t*Token-id 陣列,長度為 seq_len
seq_lenintToken 數量,1 <= seq_len <= max_seq_len
logits_outfloat*輸出 buffer,大小為 vocab_size * sizeof(float)
n_loopsint8共享 block 迭代次數,1 <= n_loops <= 64

回傳

voidlogits_out[v] 是最後位置上 token v 的未歸一化 logit。

例外 / 錯誤條件

score 相同的錯誤路徑。越界的 n_loopsmodel 為 null,或 seq_len 不在 [1, max_seq_len] 之內皆為致命。

備註

  • 記憶體優勢。 輸出 buffer 為 vocab_size 個 float(幾百 KB),相較於 scoreseq_len * vocab_size。這是降低 scoring 工作負載中 host↔device 傳輸最主要的手段。
  • score 具有相同的 forward 成本(節省完全在於輸出形狀與 forward 後的複製);若同時需要完整 logits 與最後 token 捷徑,只需呼叫 score 一次而非兩者皆呼叫。
  • 常用於在 TinyLoop 內建的 Generation API 之外實作自訂 decode loop。

範例

std::vector<float> next_logits(vocab_size);
tinyloop::score_last_token(model, prompt.data(), prompt.size(), next_logits.data(), 8);
int next_token = std::distance(next_logits.begin(),
std::max_element(next_logits.begin(), next_logits.end()));

score_logit_lens

void score_logit_lens(Model* model,
const int32_t* tokens,
int seq_len,
float* logits_out,
int n_loops = 8);

每一個1n_loops 的 loop 深度上,產生最後 token 的 logits,排列為 [n_loops, vocab_size]。這是 logit-lens 原語:它讓呼叫端觀察模型預測如何隨著更多 loop 迭代而演化。

參數

名稱型別預設描述
modelModel*已載入的模型指標。
tokensconst int32_t*Token id,長度為 seq_len
seq_lenint1 <= seq_len <= max_seq_len
logits_outfloat*輸出 buffer,大小為 n_loops * vocab_size * sizeof(float)
n_loopsint8Loop 深度上限。深度 1, 2, ..., n_loops 的 logits 皆會寫出。

回傳

void。對於 l = 1, ..., n_loops,logits_out[(l-1) * vocab_size + v]l 次 loop 迭代後最後位置上 token v 的 logit。

例外 / 錯誤條件

score 相同。此外,n_loops0 視為無效(至少需要要求一個深度)。

備註

  • 成本對 n_loops 呈超線性。 底層實作並非在所有配置下對各深度 hidden state 進行 memoize;請視其為分析原語,而非熱路徑服務原語。
  • 搭配 score_with_uncertainty 使用。 Logit-lens 輸出是 KL 為基礎的不確定性啟發式的原始素材。
  • 在訓練過的 looped 模型上進行的深度演化分析通常顯示非單調的 argmax 軌跡 — 一個 token 可能在 l=3 看似「決定」,在 l=4 翻轉,到 l=8 又重新穩定。詳見專案論文的討論。

score_with_uncertainty

void score_with_uncertainty(Model* model,
const int32_t* tokens,
int seq_len,
float* logits_hi_out,
float* kl_out,
int L_lo = 4,
int L_hi = 8);

在兩個不同 loop 深度(L_loL_hi)執行 forward,並同時輸出高深度 logits 與兩個分佈間逐位置的 KL divergence 作為不確定性訊號。

參數

名稱型別預設描述
modelModel*已載入的模型指標。
tokensconst int32_t*Token id,長度為 seq_len
seq_lenint1 <= seq_len <= max_seq_len
logits_hi_outfloat*L_hi logits 的輸出 buffer,形狀 [seq_len, vocab_size]
kl_outfloat*逐位置 KL divergence 的輸出 buffer,形狀為 [seq_len] 個 float。每個值為 KL(softmax(logits_hi) || softmax(logits_lo))
L_loint4低深度 loop 次數。1 <= L_lo < L_hi
L_hiint8高深度 loop 次數。L_lo < L_hi <= 64

回傳

void。高深度 logits 寫入 logits_hi_out;逐位置 KL 值寫入 kl_out

例外 / 錯誤條件

score 相同,另加:

  • L_lo >= L_hi 無效。
  • L_loL_hi 不在 [1, 64] 內皆無效。

備註

  • 兩次 forward pass。 計算成本大約為 L_lo + L_hi 次 loop-block 運算,相較於單一深度 score 的 L_hi。請為額外計算保留預算。
  • KL 為 high-to-low。 依慣例此呼叫回報 KL(p_hi || p_lo) — 高 KL 意謂低深度分佈被高深度分佈「驚訝到」,是有用的「需要更多思考」訊號。
  • 對於想進行自適應計算但不需要明確 KL 檢視的工作負載,請見 score_with_consistency_escalation

score_with_consistency_escalation

void score_with_consistency_escalation(Model* model,
const int32_t* tokens,
int seq_len,
float* logits_out,
int* escalated_out,
int* L_used_out,
int L_lo = 8,
int L_hi = 16,
int escalate_L = 32);

自適應深度 scoring:首先以 L_lo 執行。對每個位置,若與 L_hi 的便宜一致性檢查不符,則僅將該位置重跑到 escalate_L。適用於希望在困難 token 上取得高品質、而不必在每處都付出 escalate_L 成本的工作負載。

參數

名稱型別預設描述
modelModel*已載入的模型指標。
tokensconst int32_t*Token id,長度為 seq_len
seq_lenint1 <= seq_len <= max_seq_len
logits_outfloat*輸出 buffer,形狀 [seq_len, vocab_size]。包含每個位置實際使用的 logits(若一致則為 L_lo,若提升則為 escalate_L)。
escalated_outint*輸出 buffer,形狀 [seq_len]。每個位置寫入 0/1:1 代表該位置已提升至 escalate_L,否則為 0
L_used_outint*輸出 buffer,形狀 [seq_len]。寫入每個位置實際使用的 loop 深度。
L_loint8第一輪 loop 深度。
L_hiint16一致性檢查 loop 深度。必須滿足 L_lo < L_hi
escalate_Lint32提升位置所使用的 loop 深度。必須滿足 L_hi < escalate_L <= 64

回傳

void。主要 logits 寫入 logits_out;逐位置提升旗標寫入 escalated_out;逐位置使用的 loop 深度寫入 L_used_out

例外 / 錯誤條件

score 相同,另加:

  • L_loL_hiescalate_L 任一不在 [1, 64] 之內。
  • 順序違反:必須滿足 L_lo < L_hi < escalate_L

備註

  • 最壞情況成本是每個位置都提升時的 (L_hi + escalate_L) * seq_len 次 loop-block 運算。實務上,訓練良好的模型其提升率通常較低低於 20 %。
  • 非確定性繼承自 sampling-free forward:給定 tokensL_loL_hi,提升決策是確定性的,但實務提升率取決於模型。
  • 該檢查比較 L_loL_hi 分佈在每個位置的 argmax 一致性;若需要更豐富的一致性訊號(完整 KL),請使用 score_with_uncertainty

Scoring 路徑中的狀態轉換

對所有 score 變體:

  1. Token 嵌入至模型 buffer 空間。
  2. Pre-block 執行(n_pre_blocks)。
  3. 共享 loop block 重複 n_loops 次。
  4. 依所請求的輸出模式進行最終 LN + head projection。

除非 API 明確使用 cache helpers,否則不會推進 decode cache。模型的 buffers.main 每次呼叫都會被覆寫;希望在多次 scoring 呼叫間保留 residual stream 的呼叫端,必須透過較低階 API 自行做快照(不屬於公開 scoring 表面)。

效能模型

輸出模式輸出位元組Forward 成本典型用途
scoreseq_len * vocab * 41× forward離線評估
score_last_tokenvocab * 41× forwardDecode helpers、排序
score_logit_lensn_loops * vocab * 4~1.5× forward可解釋性分析
score_with_uncertainty(seq_len + 1) * vocab * 4~2× forward主動學習、RLHF scoring
score_with_consistency_escalationseq_len * vocab * 4 + 2 * seq_len * 4L_hi + β * escalate_L forward生產級自適應計算

β 是實證提升率;在訓練良好的 1B-effective 模型上通常介於 0.050.20

取捨與注意事項

  • API 基於 token id;tokenizer 語義外於 TinyLoop。
  • 一致性/不確定性 API 依設計增加額外 forward pass — 以吞吐量換取自適應品質訊號。請視為 opt-in。
  • 對於 prefill_chunk > 0 的配置,score 類 API 必須遵守 chunk 列預算(見 生命週期與記憶體模型)。
  • 整個過程中,輸出 buffer 所有權皆由呼叫端管理 — TinyLoop 絕不會自行配置 logit 張量。