# 策展 · X (Twitter) 🔥

> 📖 本站完整內容索引（documentation index）：[llms.txt](/llms.txt)

> 作者：Rahul (@sairahul1) · 平台：X (Twitter) · 日期：2026-06-15

> 原始來源：https://x.com/sairahul1/status/2066076937806856661

## 中文摘要

# 如何從零開始打造你自己的大型語言模型（GPT 與 Claude 背後的 5 階段流程）

![這張圖表展示了大型語言模型開發的五個關鍵階段流程。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/35971d9e6ca11023.jpg)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖片呈現了一個五步驟的流程圖，說明大型語言模型的開發過程，並標註了各階段的特性：
1. Data (Where models are won)：標註為「Most important」。
2. Tokenize (Break text into pieces)。
3. Train (Predict next token)：標註為「Most expensive」。
4. Align (Make it helpful)：標註為「Most misunderstood」。
5. Evaluate (Prove it works)。

圖表底部文字說明：「Same pipeline behind GPT, Claude, and Gemini」。</div></details>

每個人都在談論大型語言模型（LLM）。

但沒人解釋它們在底層究竟是如何運作的。

GPT。Claude。Gemini。Llama。

它們全都源自同一個 5 階段流程。

一旦你理解了這個流程，你就能自己打造一個。

不是 GPT-4 的複製品。

而是一個真正能運作的語言模型。

一個能從文字中學習、生成新文字，且內容合乎邏輯的模型。

我親手打造了一個。以下就是它的運作原理。

不需要博士學位。附帶程式碼。

---

關於大型語言模型，每個人都相信的謊言

大多數人認為打造大型語言模型關鍵在於架構。

Transformer。注意力機制（Attention heads）。層（Layers）。

其實不然。

Transformer 架構是公開發表的。每個大型實驗室使用的建構模組大同小異。

如果架構是秘密，那每個人都會有 GPT-4 了。

真正的秘密在於：資料、訓練與對齊（Alignment）。

架構只佔一個段落的篇幅。其餘的一切，才是決定模型成敗的關鍵。

以下是這 5 個階段。

---

## 第 1 階段 — 資料（模型勝出的關鍵）

![經過五個資料清理與篩選步驟後，原始網路數據僅剩約 10% 作為乾淨的訓練數據，但品質提升了 10 倍。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/b3a19b2027af355e.jpg)

<details class="chart-data"><summary>展開數據表</summary><table><thead><tr><th></th><th>步驟 1</th><th>步驟 2</th><th>步驟 3</th><th>步驟 4</th><th>步驟 5</th><th>乾淨訓練數據</th></tr></thead><tbody><tr><td>原始網路數據=1,000,000 GB (250 billion pages)</td><td>Remove duplicates</td><td>Filter harmful content</td><td>Remove low-quality pages</td><td>Quality scoring</td><td>Balance data mix</td><td>~10% remains (But 10x better)</td></tr></tbody></table></details>

原始的網路文字非常髒亂。

你無法直接用它來訓練。

Common Crawl（大多數大型語言模型用來訓練的公開網路爬取資料）包含了 2,500 億個網頁，超過一百萬 GB 的資料。

但其中大部分都是垃圾。

重複的標頭。垃圾郵件。有害內容。個人資料。只有 3 個字的低品質網頁。

在開始任何訓練之前，你必須執行一套嚴苛的多步驟篩選：

→ 從原始 HTML 中提取乾淨的文字

→ 篩選有害、不宜公開（NSFW）及個人資料

→ 透過 URL、文件與行進行去重（Deduplicate）

→ 根據字數與 token 密度移除低品質文件

→ 執行基於模型的品質評分——維基百科編輯會引用這個頁面嗎？

→ 平衡程式碼、書籍、科學與網路資料的組合

結果：一個比原始資料小得多，但品質大幅提升的乾淨資料集。

必須銘記的規則：

資料品質勝過資料數量。永遠如此。

這個領域最嚴密的秘密不是架構。

而是資料是如何被清洗的。

---

## 第 2 階段 — Tokenization（將文字拆解成模型能學習的碎片）

![這張圖解說明了大型語言模型（LLM）如何將文字拆解為 Token 並轉換為數值 ID 的處理過程。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/1d699edf7be962b6.jpg)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖片展示了「Building an LLM from scratch」這段文字經過分詞（Tokenization）的過程。
1. 頂部標題為「Building an LLM from scratch」。
2. 中間有一個剪刀圖示，象徵將文字切割。
3. 右側註記：「1 token ≈ 0.75 words」。
4. 文字被拆解為七個 Token，每個 Token 對應一個數值 ID：
   - 「Build」對應 4821
   - 「ing」對應 513
   - 「an」對應 271
   - 「LL」對應 37
   - 「M」對應 44
   - 「from」對應 505
   - 「scratch」對應 8905
5. 底部說明文字：「Every token gets an ID. Model sees numbers, not words.」（每個 Token 都會獲得一個 ID。模型看到的是數字，而非文字。）</div></details>

模型從不閱讀原始文字。

它閱讀的是 token。

一個 token 不一定是一個完整的單字。它是單字的一部分——模型學會將其視為一個單位的碎片。

"playing" → ["play", "ing"] "unbelievable" → ["un", "believ", "able"] "dog" → ["dog"]

標準方法稱為位元組對編碼（Byte-Pair Encoding, BPE）。

它從單個字元開始，重複合併最常見的配對，直到擁有一個固定的詞彙表——通常為 32,000 到 100,000 個 token。

這是一個 Python 中的最小化 tokenizer：

```python
from tokenizers import Tokenizer, models, trainers, pre_tokenizers

# 初始化 BPE tokenizer
tokenizer = Tokenizer(models.BPE())
tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()

# 在你的語料庫上進行訓練
trainer = trainers.BpeTrainer(
    vocab_size=32000,
    special_tokens=["<PAD>", "<BOS>", "<EOS>", "<UNK>"]
)
tokenizer.train(files=["your_data.txt"], trainer=trainer)
tokenizer.save("tokenizer.json")

# 測試它
output = tokenizer.encode("Building an LLM from scratch is powerful")
print(output.tokens)
# ['Building', 'an', 'LL', 'M', 'from', 'scratch', 'is', 'powerful']

print(output.ids)
# [4821, 271, 3728, 44, 505, 8905, 318, 6787]
```

經驗法則：1 個 token ≈ 0.75 個單字。

1,000 個 token ≈ 750 個單字。

100k 的 context window ≈ 大約是一整本小說的長度。

---

## 第 3 階段 — 訓練（一個簡單到令人難以置信的目標）

![這張圖表說明了大型語言模型訓練過程中，透過重複預測與權重更新來優化模型的循環流程。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/db3823b6fb5d4d6e.jpg)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖片展示了一個循環訓練流程，包含四個步驟：
1. Feed tokens in：輸入詞元（例如：The cat sat on the __）。
2. Model predicts next：模型預測下一個詞（例如：mat? floor? roof?）。
3. Calculate loss：計算損失（How wrong was it?），並附有一個長條圖示意。
4. Update weights：更新權重（Get less wrong next time），並附有神經網路節點示意圖。

圖表中央標註「Repeat 100 billion times」（重複 1000 億次），下方總結「Lower loss = model understands language better」（損失越低，模型對語言的理解越好）。</div></details>

整個訓練任務聽起來太簡單，以至於讓人懷疑它是否真的強大：

預測下一個 token。

就這樣。

給定 "The cat sat on the"，預測 "mat"。

在數兆個範例中重複這個過程，奇妙的事情就會發生。

模型學會了語法。然後是事實。然後是推理。接著是如何撰寫程式碼、翻譯語言、解決數學問題。

沒人教過它這些。

這是在大規模下，透過預測下一個 token 而湧現（Emergent）出來的能力。

這是一個 PyTorch 中的最小化 decoder-only transformer——這也是每個主流大型語言模型背後的架構：

```python
import torch
import torch.nn as nn
import math

class CausalSelfAttention(nn.Module):
    def __init__(self, embed_dim, num_heads):
        super().__init__()
        self.num_heads = num_heads
        self.head_dim = embed_dim // num_heads
        self.qkv = nn.Linear(embed_dim, 3 * embed_dim, bias=False)
        self.proj = nn.Linear(embed_dim, embed_dim, bias=False)

    def forward(self, x):
        B, T, C = x.shape
        q, k, v = self.qkv(x).chunk(3, dim=-1)
        
        # 分割成多個 head
        q = q.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
        k = k.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
        v = v.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)

        # 帶有因果遮罩（causal mask）的縮放點積注意力機制
        scale = math.sqrt(self.head_dim)
        attn = (q @ k.transpose(-2, -1)) / scale
        
        # 因果遮罩：只能關注過去的 token
        mask = torch.tril(torch.ones(T, T, device=x.device))
        attn = attn.masked_fill(mask == 0, float('-inf'))
        attn = torch.softmax(attn, dim=-1)
        
        out = (attn @ v).transpose(1, 2).contiguous().view(B, T, C)
        return self.proj(out)


class TransformerBlock(nn.Module):
    def __init__(self, embed_dim, num_heads, ff_dim, dropout=0.1):
        super().__init__()
        self.attn = CausalSelfAttention(embed_dim, num_heads)
        self.ff = nn.Sequential(
            nn.Linear(embed_dim, ff_dim),
            nn.GELU(),
            nn.Linear(ff_dim, embed_dim),
            nn.Dropout(dropout)
        )
        self.ln1 = nn.LayerNorm(embed_dim)
        self.ln2 = nn.LayerNorm(embed_dim)

    def forward(self, x):
        x = x + self.attn(self.ln1(x))   # 注意力 + 殘差連接
        x = x + self.ff(self.ln2(x))     # 前饋網路 + 殘差連接
        return x


class MiniLLM(nn.Module):
    def __init__(self, vocab_size, embed_dim, num_heads,
                 ff_dim, num_layers, max_seq_len, dropout=0.1):
        super().__init__()
        self.token_emb = nn.Embedding(vocab_size, embed_dim)
        self.pos_emb = nn.Embedding(max_seq_len, embed_dim)
        self.blocks = nn.ModuleList([
            TransformerBlock(embed_dim, num_heads, ff_dim, dropout)
            for _ in range(num_layers)
        ])
        self.ln_final = nn.LayerNorm(embed_dim)
        self.output = nn.Linear(embed_dim, vocab_size, bias=False)
        self.dropout = nn.Dropout(dropout)

    def forward(self, token_ids):
        B, T = token_ids.shape
        positions = torch.arange(T, device=token_ids.device).unsqueeze(0)
        
        x = self.dropout(
            self.token_emb(token_ids) + self.pos_emb(positions)
        )
        for block in self.blocks:
            x = block(x)
        
        x = self.ln_final(x)
        return self.output(x)  # 詞彙表上的 logits


# 初始化一個小型但真實的模型
model = MiniLLM(
    vocab_size=32000,
    embed_dim=512,
    num_heads=8,
    ff_dim=2048,
    num_layers=6,
    max_seq_len=1024
)

total_params = sum(p.numel() for p in model.parameters())
print(f"Parameters: {total_params:,}")
# Parameters: 44,082,176 — 一個 44M 參數的模型
```

現在是訓練迴圈：

```python
import torch.optim as optim
from torch.nn.utils import clip_grad_norm_

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

optimizer = optim.AdamW(model.parameters(), lr=3e-4, weight_decay=0.01)
criterion = nn.CrossEntropyLoss()

def train_epoch(model, dataloader):
    model.train()
    total_loss = 0
    
    for input_ids, target_ids in dataloader:
        input_ids = input_ids.to(device)
        target_ids = target_ids.to(device)
        
        # 前向傳播
        logits = model(input_ids)
        
        # 攤平以計算損失
        loss = criterion(
            logits.view(-1, logits.size(-1)),   # (batch * seq_len, vocab)
            target_ids.view(-1)                  # (batch * seq_len)
        )
        
        # 反向傳播
        optimizer.zero_grad()
        loss.backward()
        clip_grad_norm_(model.parameters(), max_norm=1.0)  # 防止梯度爆炸
        optimizer.step()
        
        total_loss += loss.item()
    
    return total_loss / len(dataloader)
```

模型實際學到的東西：

→ 每個輸入 token 都會關注所有之前的 token

→ 因果遮罩防止模型窺探未來

→ 損失（Loss）= 模型對真實下一個 token 的「驚訝程度」

→ 損失越低 = 預測越準 = 模型正在學習語言

---

## 第 4 階段 — 對齊（將文字預測器轉變為助理）

![這張圖解說明了大型語言模型訓練過程中 SFT（監督式微調）與 RLHF（人類回饋強化學習）的運作機制。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/e633ba2f9273be50.jpg)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖表分為左右兩部分，說明模型訓練的兩個階段：

左側為 SFT (Supervised Fine-Tuning)：
- 標題：SFT
- 內容：Prompt → Response examples
- 範例列表：
  - Q: How do I reverse a list in Python? A: You can use list.reverse() ...
  - Q: What is 2 + 2? A: 4 ...
  - Q: Explain photosynthesis. A: Photosynthesis is the process where plants ...
- 說明：Learn the FORMAT of good answers
- 備註：~5,000 examples enough

右側為 RLHF (Reinforcement Learning from Human Feedback)：
- 標題：RLHF
- 內容：
  - Answer A: Photosynthesis is the process used by plants to convert light into energy. It ...
  - Answer B: Photosynthesis basically means plants eat sunlight and make food lol. It ...
- 互動：一個手勢圖示比出「讚」，指向 Answer A。
- 說明：Reward signal，指向機器人圖示。
- 總結：Learn what HUMANS prefer

底部流程：
- Pretrained model → ChatGPT / Claude</div></details>

預訓練完成後，你得到了一個令人印象深刻但對聊天毫無用處的模型。

問它一個問題，它可能會回你另外三個問題。

因為預測下一個 token 並不代表理解你想要什麼。

兩個步驟可以解決這個問題。

步驟 1：監督式微調（Supervised Fine-Tuning, SFT）

向模型展示數千個範例：

prompt → 理想的回應

模型學會模仿優質回答的格式。

令人驚訝的是：你只需要很少的資料。

幾千個範例就足夠了，因為知識已經存在於預訓練模型中。

SFT 只是教它以正確的格式表達這些知識。

```python
# SFT 訓練範例結構
sft_examples = [
    {
        "prompt": "Explain what an API is in simple terms.",
        "response": "An API is like a waiter in a restaurant. You (the app) tell the waiter (API) what you want. The waiter goes to the kitchen (server), gets it, and brings it back. You never go to the kitchen directly."
    },
    {
        "prompt": "What is the capital of France?",
        "response": "The capital of France is Paris."
    }
    # 幾千個這樣的範例就夠了
]

# 格式為：<prompt> [SEP] <response> <EOS>
# 在這些配對上微調預訓練模型
# 與預訓練相同的訓練迴圈 — 只是資料不同
```

步驟 2：RLHF（基於人類回饋的強化學習）

SFT 教導格式。RLHF 教導偏好。

模型生成兩個答案。人類選擇較好的一個。這些偏好會訓練一個獎勵模型（Reward model）。大型語言模型會被最佳化以最大化該獎勵。

這就是為什麼 ChatGPT 和 Claude 感覺像助理，而不是隨機的文字產生器。

沒有 RLHF：

→ 流暢。有能力。但不可靠。

→ 自信地胡說八道。

→ 不知道何時該說「我不知道」。

有了 RLHF：

→ 有幫助。清晰。安全。

→ 學會了什麼才是「好的回答」。

---

## 第 5 階段 — 評估（證明它真的有效）

![圖表說明在預訓練階段可透過困惑度（Perplexity，數值自 847 降至 23）評估模型學習成效，但在對齊階段後該指標失效，需轉換為 MMLU、Chatbot Arena 及 AlpacaEval 等評估方法。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/12efc44394bda5ab.jpg)

<details class="chart-data"><summary>展開數據表（1）During Pretraining</summary><table><thead><tr><th></th><th>起始</th><th>中期</th><th>結束</th></tr></thead><tbody><tr><td>\nPerplexity 趨勢</td><td>847</td><td>124</td><td>23\n\n</td></tr></tbody></table></details>

在不測量的情況下建立模型等於是在猜測。

在預訓練期間 — 測量困惑度（Perplexity）。

困惑度測量模型對真實文字有多「驚訝」。

困惑度越低 = 模型預測文字越好 = 它正在學習。

在 2017 年到 2023 年之間，頂尖模型的困惑度從約 70 個可能的 token 降低到不到 10 個。

```python
import torch
import math

def calculate_perplexity(model, dataloader, device):
    model.eval()
    total_loss = 0
    total_tokens = 0
    criterion = nn.CrossEntropyLoss(reduction='sum')
    
    with torch.no_grad():
        for input_ids, target_ids in dataloader:
            input_ids = input_ids.to(device)
            target_ids = target_ids.to(device)
            
            logits = model(input_ids)
            
            loss = criterion(
                logits.view(-1, logits.size(-1)),
                target_ids.view(-1)
            )
            total_loss += loss.item()
            total_tokens += target_ids.numel()
    
    avg_loss = total_loss / total_tokens
    perplexity = math.exp(avg_loss)
    return perplexity

# 範例輸出進度：
# Epoch 1: Perplexity = 847.3  (模型幾乎什麼都不知道)
# Epoch 5: Perplexity = 124.6  (越來越好)
# Epoch 20: Perplexity = 23.4  (真正學會了語言)
```

對齊之後 — 困惑度就不再適用了。

微調後的模型在困惑度上的表現可能變差，但卻變得更有用。

你需要人類基準測試：

→ MMLU：57 個學科，選擇題 — 測量知識

→ Chatbot Arena：人類盲測比較兩個模型並投票 — 測量偏好

→ AlpacaEval：大型語言模型評審大型語言模型 — 與人類評審有 98% 的相關性，成本僅 10 美元

老實說：沒有單一分數能完全捕捉一個好模型。

同一個模型在同一個基準測試上，僅僅因為 prompt 格式不同，分數就可能從 0.637 變成 0.488。

評估確實很難。

而且還沒有人完全解決它。

---

## 如何從你的模型生成文字

![本圖展示了語言模型的自迴歸生成流程，並說明不同溫度（Temperature）參數（Cold 0.1、Warm 0.8、Hot 1.5）如何影響下一個 Token 的採樣隨機性，溫度越高則生成結果越具多樣性且不可預測。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/3bdf5b57eeb86ff9.jpg)

<details class="chart-data"><summary>展開數據表（1）生成循環</summary><table><thead><tr><th>項目</th><th>數值</th></tr></thead><tbody><tr><td>輸入: "The cat sat on the" -&gt; 模型前向傳播 -&gt; 輸出機率分佈: mat (最高, 帶勾), floor, roof, bed -&gt; 採樣 Token: "mat" -&gt; 新輸入: "The cat sat on the mat" (重複直到完成)</td><td></td></tr></tbody></table></details>

模型已經訓練好了。

現在讓它生成一些東西。

```python
def generate(model, tokenizer, prompt, max_new_tokens=100,
             temperature=0.8, device='cuda'):
    model.eval()
    
    # 將 prompt 編碼為 token ID
    token_ids = tokenizer.encode(prompt).ids
    input_tensor = torch.tensor(token_ids, dtype=torch.long,
                                device=device).unsqueeze(0)
    
    with torch.no_grad():
        for _ in range(max_new_tokens):
            
            # 只保留最後 max_seq_len 個 token (context window)
            context = input_tensor[:, -1024:]
            
            # 前向傳播
            logits = model(context)
            
            # 只取得最後一個 token 的 logits
            next_token_logits = logits[:, -1, :]
            
            # 應用 temperature (越高 = 越有創意)
            next_token_logits = next_token_logits / temperature
            
            # 從機率分佈中採樣
            probs = torch.softmax(next_token_logits, dim=-1)
            next_token = torch.multinomial(probs, num_samples=1)
            
            # 追加並繼續
            input_tensor = torch.cat([input_tensor, next_token], dim=1)
            
            # 在序列結束 token 處停止
            if next_token.item() == tokenizer.token_to_id("<EOS>"):
                break
    
    # 解碼回文字
    generated_ids = input_tensor[0].tolist()
    return tokenizer.decode(generated_ids)


# 測試它
output = generate(
    model, tokenizer,
    prompt="The most important thing about machine learning is",
    max_new_tokens=100,
    temperature=0.8
)
print(output)
```

Temperature 控制創意：

→ temperature = 0.1 → 安全、可預測、重複性高

→ temperature = 0.8 → 自然、多樣、良好的預設值

→ temperature = 1.5 → 有創意、令人驚喜、有時語無倫次

---

## 完整的流程看起來是什麼樣子

![這張圖表對比了人工智慧模型開發過程中，「Before（原始狀態）」與「After（優化後狀態）」的五個關鍵階段。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/0af3a394b826de21.jpg)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖表分為左右兩欄，左側為「Before ❌」，右側為「After ✔️」，底部有一行總結文字。

各階段對比內容如下：
1. 資料處理：左側為「Raw internet dump」（原始網路數據堆疊），右側為「Clean training data」（乾淨的訓練資料）。
2. 資料格式：左側為「Words and sentences」（單字與句子），右側為「Token IDs the model reads」（模型讀取的 Token ID）。
3. 模型狀態：左側為「Random weights」（隨機權重），右側為「Model that understands language」（理解語言的模型）。
4. 模型功能：左側為「Text predictor」（文字預測器），右側為「Helpful assistant」（有幫助的助手）。
5. 驗證機制：左側為「No idea if it works」（不確定是否有效），右側為「Measured, benchmarked, proven」（經過測量、基準測試與驗證）。

底部總結文字：「Skip one stage. The whole thing breaks.」（跳過任何一個階段，整個系統就會崩潰。）</div></details>

之前：原始網路文字，100 萬 GB，完全無法使用。

第 1 階段後：乾淨的篩選資料集，準備好進行訓練。

之前：原始文字，對模型來說毫無意義。

第 2 階段後：帶有 ID 的 token，這是模型的母語。

之前：隨機權重，輸出垃圾。

第 3 階段後：一個理解語言模式的模型。

之前：一個用更多問題來回答問題的文字預測器。

第 4 階段後：一個遵循指令且安全的助理。

之前：不知道模型是否真的好用。

第 5 階段後：基準測試、困惑度分數、人類評估。

每個階段都建立在前一個階段之上。

跳過任何一個，整個東西就會崩潰。

---

## 導致大型語言模型專案失敗的 5 個錯誤

1. 執著於架構。

Transformer 是標準化的。已發表的。被複製的。

架構是最不重要的部分。

2. 將資料視為商品。

無論運算能力如何，髒亂的資料會限制你的上限。

頂尖實驗室在資料清洗上花費的精力比模型設計還多。

3. 跳過擴展（Scaling）數學。

對於資料量來說太大的模型會訓練不足，浪費運算資源。

最佳比例：每個參數約 20 個 token 的訓練資料。

4. 止步於 SFT。

微調後的模型只是在模仿。沒有 RLHF，它永遠學不會人類真正偏好什麼。

5. 對齊後仍信任困惑度。

訓練後會改變分佈。

一旦你執行了 SFT，困惑度就不再有意義。

立即切換到人類基準測試。

---

## 不願面對的真相

優秀的大型語言模型不是訓練出來的。

它是被設計出來的。

5 個階段。不是 1 個。

架構只是第 3 階段裡的一個段落。

所有重要的事情都在另外四個階段裡。

資料品質。擴展數學。對齊。誠實的評估。

這就是 GPT-4 與業餘模型之間的區別。

兩個擁有相同架構的實驗室會產生截然不同的模型。

架構是共享的。

但真正重要的一切都不是。

---

從這裡開始

想要自己執行嗎？這是最小化的設定：

```plaintext
# 完整的最小化大型語言模型訓練設定
# 需求：pip install torch tokenizers datasets

# 1. 取得資料
from datasets import load_dataset
dataset = load_dataset("wikitext", "wikitext-103-v1", split="train")
text = "\n\n".join([t.strip() for t in dataset['text'] if t.strip()])

# 2. 訓練 tokenizer
from tokenizers import Tokenizer, models, trainers, pre_tokenizers
tokenizer = Tokenizer(models.BPE())
tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
trainer = trainers.BpeTrainer(vocab_size=8000,
                               special_tokens=["<PAD>","<BOS>","<EOS>"])
with open("corpus.txt", "w") as f:
    f.write(text[:5_000_000])  # 從 5MB 開始
tokenizer.train(["corpus.txt"], trainer)

# 3. 建立模型 (使用上述第 3 階段的 MiniLLM 類別)
model = MiniLLM(
    vocab_size=8000,
    embed_dim=256,       # 小但真實
    num_heads=8,
    ff_dim=1024,
    num_layers=4,
    max_seq_len=256
)
# 約 15M 參數 — 在筆電 GPU 上幾小時內即可完成訓練

# 4. 訓練 (使用上述第 3 階段的 train_epoch)
# 5. 生成 (使用上述第 5 階段的 generate())

print("Your LLM is running.")
```

從小規模開始。15M 參數。WikiText 資料集。Google Colab 上的免費 GPU。

看著困惑度在幾個小時內從 800 降到 50。

那個下降的過程就是模型在學習語言。

即時發生。

那一刻，一切都豁然開朗。

---

## 現在讓它變得有用：打造一個真正的利基（Niche）大型語言模型

![這張圖表展示了透過相同的五階段流程，利用不同領域的資料集訓練出各類專業 AI 模型的概念。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/6e2c38b4f3bb32ee.jpg)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖片呈現了一個名為「Same 5-Stage Pipeline」的流程圖，包含五個階段：Data → Tokenize → Train → Align → Evaluate。

此流程向下延伸出五種不同應用場景的 AI 模型，每個模型下方皆標示了其使用的資料來源與獲利符號（$）：
1. Coding Assistant：使用 GitHub + Stack Overflow 資料。
2. SQL Generator：使用 Spider + WikiSQL 資料。
3. Legal Summarizer：使用 Court Opinions + Contracts 資料。
4. Medical Explainer：使用 PubMed + MedQA 資料。
5. Product Copywriter：使用 Top Shopify Stores 資料。

圖表底部總結了一句核心概念：「Change the data. Get a different expert.」（改變資料，就能獲得不同的專家模型）。</div></details>

WikiText 是一個學習用的資料集。

真正的獲利點——以及真正的樂趣——在於針對特定領域進行訓練，並看著你的模型成為該領域的專家。

這裡有 5 個你現在就可以打造的利基市場。相同的流程。不同的資料。

## 利基 1 — 程式撰寫助理大型語言模型（主要範例：影響力最大，效果最顯著）

這是第一個要打造的模型。

痛點是普遍存在的。每個開發者都遇過：

→ 你盯著一個無法運作的函式。

→ Stack Overflow 有 12 個答案，全都是 2014 年的。

→ 你貼到 ChatGPT，得到一個幾乎正確但有問題的答案。

一個在正確資料上訓練過的程式撰寫大型語言模型，可以原生、離線地完成這件事，並且針對你的技術堆疊進行調整。

資料：

```python
from datasets import load_dataset

# 程式碼資料集 — 來自 GitHub 的 6.4M 個 Python 檔案
code_dataset = load_dataset("codeparrot/github-code",
                             languages=["Python"],
                             streaming=True,
                             split="train")

# Stack Overflow 問答對 — 真實的開發者問題 + 被採納的答案
so_dataset = load_dataset("koutch/stackoverflow_python",
                           split="train")

# The Stack — 30 種程式語言，已清洗並去重
the_stack = load_dataset("bigcode/the-stack",
                          data_dir="data/python",
                          split="train",
                          streaming=True)
```

訓練配對看起來像這樣：

```python
# 格式 1：程式碼補全
# 輸入：函式簽名 + docstring
# 目標：完整實作

input_example = """
def calculate_compound_interest(principal, rate, time, n=12):
    \"\"\"
    Calculate compound interest.
    Args:
        principal: Initial investment amount
        rate: Annual interest rate (as decimal, e.g. 0.05 for 5%)
        time: Time period in years
        n: Compounding frequency per year (default: monthly)
    Returns:
        Final amount after compound interest
    \"\"\"
"""

target_example = """
    amount = principal * (1 + rate/n) ** (n * time)
    return round(amount, 2)
"""

# 格式 2：錯誤 → 修復配對 (來自 Stack Overflow)
input_example_2 = """
# ERROR: TypeError: unsupported operand type(s) for +: 'int' and 'str'
user_age = input("Enter your age: ")
next_year_age = user_age + 1
print(f"Next year you will be {next_year_age}")

# Fix this code:
"""

target_example_2 = """
user_age = int(input("Enter your age: "))  # 將字串轉換為整數
next_year_age = user_age + 1
print(f"Next year you will be {next_year_age}")
"""

# 格式 3：自然語言 → 程式碼 (指令遵循)
input_example_3 = """
# Write a Python function that:
# - Takes a list of dictionaries
# - Filters by a given key-value pair
# - Returns sorted results by a specified field
"""

target_example_3 = """
def filter_and_sort(data, filter_key, filter_value, sort_field):
    filtered = [item for item in data
                if item.get(filter_key) == filter_value]
    return sorted(filtered, key=lambda x: x.get(sort_field, ''))
"""
```

這能賣出的前後對比：

訓練前：

> Prompt: "Write a Python decorator that retries a function on failure"
Output: "A decorator is a design pattern in Python that allows..."
# 通用。沒用。教科書式的回答。

在 GitHub + Stack Overflow 上訓練 10 小時後：

Prompt: "Write a Python decorator that retries a function on failure"
Output:


```python
import time
from functools import wraps

def retry(max_attempts=3, delay=1.0, exceptions=(Exception,)):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    if attempt == max_attempts - 1:
                        raise
                    time.sleep(delay * (attempt + 1))
        return wrapper
    return decorator

# 用法：
@retry(max_attempts=3, delay=0.5, exceptions=(ConnectionError,))
def fetch_data(url):
    ...
# 正確。生產環境就緒。包含指數退避（Exponential backoff）。
```

模型學會了資深開發者會寫出的程式碼。

不是因為被告知，而是因為閱讀了 600 萬個 Python 檔案和每一個被採納的 Stack Overflow 答案。

![此圖比較了 AI 模型在經過 GitHub 與 Stack Overflow 資料訓練前後，對於程式設計請求的回答品質差異。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/5ab2d78aa86d94bc.jpg)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖表分為左右兩側，中間標示「10 hours of training on GitHub + Stack Overflow」。

左側「Before Training」（紅色框）：
- 使用者輸入：「Write a retry decorator」
- AI 回覆：「A decorator is a design pattern...」（後接大量波浪線代表冗長且無用的解釋）
- 評語：「Generic. Useless.」

右側「After Training」（綠色框）：
- 使用者輸入：「Write a retry decorator」
- AI 回覆：提供了一段完整的 Python 程式碼，包含 `retry` 裝飾器定義，支援 `max_attempts`、`delay` 及 `exceptions` 參數，並實作了指數退避（exponential backoff）邏輯。
- 評語：「Production-ready. First try.」

底部標語：「6.4 million Python files → one expert model」</div></details>

---

## 利基 2 — SQL 查詢生成器

痛點：每個非技術背景的創辦人都有他們無法存取的資料。

資料就在那裡。在他們的資料庫裡。他們只是不會寫查詢語句。

他們用簡單的英文描述他們想要什麼。你的模型寫出 SQL。

資料：

```python
# Spider 資料集 — 10,000+ 個帶有自然語言描述的 SQL 查詢
# 涵蓋 138 個領域的 200 個資料庫
spider = load_dataset("spider", split="train")

# WikiSQL — 80,654 個自然語言 + SQL 配對
wikisql = load_dataset("wikisql", split="train")

# 訓練配對看起來像這樣：
example = {
    "question": "Show me all users who signed up in the last 30 days and made at least one purchase",
    "sql": """
        SELECT u.id, u.email, u.created_at, COUNT(p.id) as purchase_count
        FROM users u
        JOIN purchases p ON u.id = p.user_id
        WHERE u.created_at >= NOW() - INTERVAL '30 days'
        GROUP BY u.id, u.email, u.created_at
        HAVING COUNT(p.id) >= 1
        ORDER BY u.created_at DESC;
    """
}
```

前後對比：

```python
# 非技術創辦人輸入：
"Which of my customers spent the most money last quarter
 but haven't bought anything in the last 60 days?"

# 模型輸出：
SELECT
    c.customer_id,
    c.email,
    SUM(o.total_amount) as q_spend,
    MAX(o.created_at) as last_order_date
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.created_at BETWEEN DATE_TRUNC('quarter', NOW() - INTERVAL '3 months')
                        AND DATE_TRUNC('quarter', NOW())
GROUP BY c.customer_id, c.email
HAVING MAX(o.created_at) < NOW() - INTERVAL '60 days'
ORDER BY q_spend DESC
LIMIT 20;
```

誰會付錢：每個 SaaS 創辦人、每個電子商務經營者、每個坐在企業主與資料庫之間的分析師。

他們會毫不猶豫地每月支付 20 美元。

---

## 利基 3 — 美國法律文件摘要器

痛點：一份 40 頁的合約。一份租賃協議。一份保密協議（NDA）。

大多數人在不理解的情況下就簽署了。

律師閱讀它每小時收費 300 美元。

你的模型在 3 秒內讀完。

資料：

```python
# Free Law Project — 數百萬份美國法院意見書，公共領域
free_law = load_dataset("free-law/courtlistener-opinion-clustering",
                         split="train")

# MultiLegalPile — 跨美國司法管轄區的法律文字
multi_legal = load_dataset("joelniklaus/multi_legal_pile",
                            languages=["en"],
                            split="train")

# 訓練配對看起來像這樣：
example = {
    "input": """
    SECTION 12.4 INDEMNIFICATION. Licensee shall defend, indemnify,
    and hold harmless Licensor and its officers, directors, employees,
    agents, and successors from and against any and all losses, damages,
    liabilities, deficiencies, claims, actions, judgments, settlements,
    interest, awards, penalties, fines, costs, or expenses of whatever
    kind, including reasonable attorneys' fees, that are incurred by
    Licensor arising out of or relating to Licensee's breach of any
    representation, warranty, covenant, or obligation under this Agreement...
    [continues for 3 more paragraphs of dense legalese]
    """,

    "summary": """
    PLAIN ENGLISH: If you break any part of this contract and it causes
    the other party legal trouble or financial loss, YOU pay for everything —
    their lawyers, court costs, damages, all of it. This is a broad
    indemnification clause that heavily favors the licensor.

    RED FLAGS:
    • "any and all losses" — extremely broad, no cap on liability
    • "reasonable attorneys' fees" — you pay their legal bills too
    • No carve-out for licensor's own negligence

    NEGOTIATE: Ask to add "except to the extent caused by Licensor's
    gross negligence or willful misconduct" before signing.
    """
}
```

讓這一切值得付費的輸出格式：

```python
Input: [貼上任何美國合約條款]

Output:
━━━━━━━━━━━━━━━━━━
PLAIN ENGLISH SUMMARY
━━━━━━━━━━━━━━━━━━
[任何人都能理解的 2-3 句話]

RED FLAGS 🚩
• [此條款中的特定風險]
• [它允許對方做什麼]

MISSING PROTECTIONS ⚠️
• [平衡的條款應該包含什麼]
• [律師會要求增加什麼]

RISK LEVEL: High / Medium / Low
━━━━━━━━━━━━━━━━━━
```

誰會付錢：審查客戶合約的自由工作者、小企業主、審查租約的租屋族、簽署第一份 SaaS 協議的創業家。

市場就是所有負擔不起律師費，但又必須簽署文件的人。

---

## 利基 4 — 醫療症狀解釋器

痛點：你用 Google 搜尋症狀，WebMD 告訴你只剩 3 天可活。

一個在臨床筆記和病患教育資料上訓練過的醫療大型語言模型，會做一些不同的事。

它用簡單的英文解釋症狀的實際含義。沒有災難化。

```python
# PubMed 摘要 — 3,500 萬份醫學研究論文
pubmed = load_dataset("pubmed", split="train")

# MedQA — 美國醫師執照考試問題 + 答案
medqa = load_dataset("bigbio/med_qa", split="train")

# 訓練配對格式：
example = {
    "patient_question": "I've had a dull ache behind my left eye for 3 days. It gets worse when I lean forward. No fever. What could this be?",

    "clinical_explanation": """
    Based on your description — dull ache behind one eye, worsening when leaning forward,
    no fever, lasting 3 days — the most likely explanation is:

    MOST LIKELY: Sinusitis (sinus pressure)
    The maxillary or frontal sinuses sit directly behind and above your eyes.
    Inflammation or congestion increases pressure when you change head position.
    The fact that it worsens leaning forward is a classic sinusitis pattern.

    ALSO POSSIBLE: Tension headache, eye strain, or mild migraine variant

    LESS LIKELY but worth noting: If you also have vision changes, nausea,
    or the pain is sudden and severe — see a doctor same day.

    WHAT TO TRY: Saline nasal rinse, steam inhalation, ibuprofen for pressure relief.
    If no improvement in 7-10 days or fever develops → see a doctor.

    NOTE: This is general health information, not a diagnosis.
    Always consult a licensed physician for medical advice.
    """
}
```

關鍵：每個回答都以明確的免責聲明和升級訊號結尾。

這就是將有用的健康資訊與危險建議區分開來的地方。

---

## 利基 5 — 電子商務產品描述撰寫器

痛點：一家擁有 500 種產品的 Shopify 商店。每個描述都是沒人會讀的規格清單牆。

好的產品描述只做一件事：讓人產生感覺。

一個在轉換率高的產品文案上訓練過的大型語言模型，會精確地學會哪些詞彙能推動點擊。

```python
# 訓練資料結構：
example = {
    "product_specs": """
    Product: Ceramic Coffee Mug
    Material: Stoneware ceramic
    Capacity: 14oz
    Dimensions: 3.5" diameter x 4.2" height
    Colors: Matte black, cream white, sage green
    Dishwasher safe: Yes
    Microwave safe: Yes
    Weight: 0.8 lbs
    """,

    "high_converting_description": """
    Some mornings deserve better than a paper cup.

    This is the mug that stays on your desk. The one your coworkers
    ask about. Heavy enough to feel intentional. Smooth enough to
    actually enjoy holding at 7am.

    14oz — the right amount. Not the novelty bucket. Not the tiny
    espresso thing. The one you actually finish.

    Matte stoneware that doesn't show fingerprints. Dishwasher safe
    because life is short. Three colors that work with any kitchen
    that isn't trying too hard.

    You already have mugs.
    You don't have this one.
    """,

    "meta_description": "Stoneware ceramic mug, 14oz, matte finish. Dishwasher and microwave safe. The coffee mug that stays.",

    "keywords": ["ceramic coffee mug", "stoneware mug", "matte black mug",
                 "14oz mug", "minimalist coffee mug", "handmade style mug"]
}
```

資料來源：爬取流量最高的前 1,000 家 Shopify 商店。提取產品標題、規格和描述。過濾出評論數高的產品——這些描述已被證明可以轉換。在這些資料上進行訓練。

你的模型學會了規格清單與銷售文案之間的區別。

---

這 5 個利基市場的共同模式

看看它們共享什麼：

→ 使用者每天感受到的明確、具體的痛點

→ 已經存在且公開可用的資料來源

→ 該行業內任何人都能立即看出的前後對比

→ 因為替代方案需要花費更多時間或金錢，所以使用者願意付費

這 5 階段流程對每一個利基市場都是一樣的。

你只改變一件事：訓練資料。

相同的 tokenizer 設定。相同的 Transformer 架構。相同的訓練迴圈。相同的評估方法。

不同的資料 → 不同的專家 → 不同的產品。

這就是槓桿作用。

一個流程。五個產品。五個收入來源。

---

如果這篇文章對你有幫助：

→ 轉發分享給每個正在學習 AI 的開發者

→ 追蹤 @sairahul1 以獲取更多類似的深度解析

→ 加入書籤 — 程式碼是可以運作的，今晚就試試看

我撰寫關於 AI、打造產品以及在你睡覺時也能運作的系統的文章。

## 標籤

教學資源, LLM, AIGC, OpenAI, Anthropic, GPT, Claude
