iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
自我挑戰組

模型真的會推理嗎?30 天從 Base Model 打造可驗證的小型推理模型系列 第 4

從預訓練 Qwen3 0.6B Base 開始的「From Scratch」

  • 分享至 

  • xImage
  •  

我在 Day 03 留下的問題是那些已經下載好的權重裡面,到底裝著什麼。讀取數億個浮點數想當然是不太可行,基本上就是駭客任務》裡的數碼瀑布是另一件事,應該知道的是:當一段文字進入模型以後,會依序經過哪些元件,又會以什麼形狀抵達下一站。

先前的 Build a Large Language Model (From Scratch) 或類似的教,學經常是從隨機權重開始預訓練一個語言模型;但 Sebastian Raschka 這次的 Build a Reasoning Model (From Scratch) 並沒有用同樣的方法,根據他的官方說明程式碼庫,旅程的起點是一個已經預訓練完成的 Base LLM,接著才自己實作推理時擴展、強化學習與蒸餾等方法,把推理能力一層一層加上去。

換句話說,這裡的 From Scratch 修飾的是「推理方法」,不是「從零學會語言」。我們不會假裝 Qwen3 0.6B Base 是一張白紙,它早已從大量文本學會語言的統計結構,也已經可以預測下一個 token;如同 Day 03 所看到的,它還不是一個按照對話規則回答問題的助理,更不是我們最後想取得的推理模型,我們得真正地重新出發。

我使用的是官方的 Qwen/Qwen3-0.6B-Base。考慮到即使名稱相同,線上的模型庫仍可能更新,因此程式裡另外固定了實際使用的 revision。如此一來,隔了一段時間重新執行時,拿到的仍會是同一組設定、權重與 tokenizer,而不是名字相同、內容卻已悄悄改變的版本。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

MODEL_ID = "Qwen/Qwen3-0.6B-Base"
REVISION = "da87bfb608c14b7cf20ba1ce41287e8de496c0cd"

tokenizer = AutoTokenizer.from_pretrained(
    MODEL_ID,
    revision=REVISION,
)

model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    revision=REVISION,
    dtype=torch.bfloat16,
    attn_implementation="sdpa",
).to("cuda")

model.eval()

這是我這次實際使用的 BF16 與 CUDA 設定,model.eval() 則會關閉只在訓練時需要的行為。固定 revision、推論模式和生成方式,並不保證任何兩張 GPU 都會逐 bit 產生完全相同的數值,但至少先固定了最容易悄悄漂移的幾個條件。對一個預計延續三十天、途中還要不斷修改模型的實驗、專題來說,這點倒是頗重要的。

載入完成後,可以先來觀察模型規格和大小。Qwen3 0.6B 的「0.6B」是一個方便辨識的級距名稱,實際載入後可以數到 596,049,920 個參數。它的主體有 28 層,每個 token 在模型內部會以 1,024 維向量表示,前饋網路的中間維度是 3,072;注意力部分有 16 個 query heads 與 8 個 key-value heads,而輸出端則保留 151,936 個候選位置。

print(f"parameters: {sum(p.numel() for p in model.parameters()):,}")
print(f"layers: {model.config.num_hidden_layers}")
print(f"hidden size: {model.config.hidden_size:,}")
print(f"intermediate size: {model.config.intermediate_size:,}")
print(
    "attention heads / KV heads: "
    f"{model.config.num_attention_heads} / "
    f"{model.config.num_key_value_heads}"
)
print(f"output vocabulary: {model.config.vocab_size:,}")
parameters: 596,049,920
layers: 28
hidden size: 1,024
intermediate size: 3,072
attention heads / KV heads: 16 / 8
output vocabulary: 151,936

真正有意思的是文字通過模型時,模型內的架構和元件會如何串接在一起。讓我們再次使用同樣的提示詞 The answer to 2 + 3 is,tokenizer 將它切成 9 個 token。接著不直接呼叫完整模型,而是先讓輸入通過 Qwen3 的 decoder backbone,再自行送進最後的 language-model head。

inputs = tokenizer(
    "The answer to 2 + 3 is",
    return_tensors="pt",
).to(model.device)

with torch.inference_mode():
    hidden = model.model(
        **inputs,
        use_cache=False,
    ).last_hidden_state

    split_logits = model.lm_head(hidden)
    direct_logits = model(
        **inputs,
        use_cache=False,
    ).logits

print("input_ids:    ", list(inputs["input_ids"].shape))
print("hidden states:", list(hidden.shape))
print("logits:       ", list(split_logits.shape))
print(
    "max difference:",
    (split_logits - direct_logits).abs().max().item(),
)
input_ids:     [1, 9]
hidden states: [1, 9, 1024]
logits:        [1, 9, 151936]
max difference: 0.0

第一個 1 表示這次只有一筆輸入,9 是提示詞被切成的 token 數量。經過 28 層 decoder 之後,每一個位置都得到一個 1,024 維的 hidden state;最後的 LM head 再把它投影成 151,936 個 logits,也就是模型對下一個 token 各種可能性的原始分數。至於這些分數如何變成真正被選中的 token,我預計明天再來仔細說明,今天的部分只需要確認:把完整模型拆成 backbone 與 LM head 兩段以後,算出的結果並沒有改變。

最後再讓它實際生成一次,這裡仍刻意使用 greedy decoding,每一步都選分數最高的 token,因為今天需要的是一把排除隨機差異的直尺,而不是替現代推理模型推薦生成設定。相同提示詞限制生成 12 個新 token,Transformers 內建的 generate() 與我把 decoder、LM head 和逐 token 迴圈分開寫的版本,得到了完全相同的 token IDs,也產生同一段文字:

with torch.inference_mode():
    output_ids = model.generate(
        **inputs,
        max_new_tokens=12,
        do_sample=False,
        pad_token_id=tokenizer.eos_token_id,
    )

new_ids = output_ids[0, inputs["input_ids"].shape[1]:]
print(tokenizer.decode(new_ids))
 5. What is the answer to 2 +

它先答出了 5,接著又開始重複問題。這不是一個特別精準的對話結果,卻很符合本日文章的目的。我們證明的只有同一組預訓練權重已被正確載入,文字能以預期的形狀流過模型,而且拆開後的計算仍與原本一致;這些結果沒有證明它的推理能力變好,也沒有把 Base model 一夜之間變成 reasoning model,而是:Qwen3 0.6B Base 對我們而言已經不再是一個按下 from_pretrained() 便憑空出現的黑盒子了。明天我們會從最後那 151,936 個 logits 繼續往下走,看看它們如何決定下一個 token,也看看 autoregressive generation 為何總是一次只向前一步,以及 attention 與 KV cache 在這段過程裡究竟省下了什麼。


上一篇
Base、Instruct、Reasoning:同一骨架上的不同後訓練
下一篇
從 logits 到 token:自迴歸生成、attention 與 KV cache
系列文
模型真的會推理嗎?30 天從 Base Model 打造可驗證的小型推理模型5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言