跳到主要內容

NLP Day 21 實戰:微調一個領域問答模型

NLP Day 21 實戰:微調一個領域問答模型

執行需求:Colab T4 可跑。本篇在 Colab 免費 T4(16 GB VRAM)從零到尾微調一個領域問答模型。我們用 DRCD(Delta Reading Comprehension Dataset,Shao 等人 2018)做中文閱讀理解資料,這是台達研究院與中研院合作發布的公開資料集(GitHub 釋出為 CC BY-NC-SA 4.0,研究與非商業用途;商用需另尋替代資料或取得授權),收錄 10,014 篇文章、約 34,196 個 QA 對,是中文 MRC 的經典基準。把 DRCD 轉成 Alpaca 格式(Stanford Alpaca 的 instruction/input/output 三欄,2023 年發布、CC BY-NC 4.0)後,用 QLoRA 對 Qwen/Qwen2.5-1.5B(阿里 2024-09 發布)做微調。整篇固定在 2025 年 3–5 月的版本下:PyTorch 2.6、transformers 4.49、peft 0.14、trl 0.15、bitsandbytes 0.45、datasets 3.2、accelerate 1.0。1.5B 模型在 T4 上跑 QLoRA 訓練約 25–35 分鐘(3,000 筆、約 1 epoch)。

引言

Day 17 我們把 LoRA 與 QLoRA 的 API 走過一輪:LoraConfig、get_peft_model、SFTTrainer,還用 Llama 3.2 1B 在 Dolly-15k 的 500 筆中文樣本上跑了 demo。Day 18 我們把「指令資料的準備與清洗」系統化:去重、過濾、翻譯、JSONL 輸出。但這兩篇都還是「通用指令遵循」的微調,沒有觸及領域問答這個真實應用最頻繁的場景。今天要把前面累積的工具全部用上,從 DRCD 原始格式開始,轉成 Alpaca、用 QLoRA 微調 Qwen 2.5 1.5B、再用 Day 19 的 ROUGE-L 與 F1 對開發集做評估,最後展示怎麼把模型部署成本地推論。

領域問答是 LLM 微調最具代表性的應用。通用 LLM 雖然什麼都會一點,但對「特定領域」(醫療、法律、金融、客服)的問題往往給出過於籠統、甚至編造的答案。透過領域內的 QA 對微調,模型可以學到「這個領域的常見問法、答案的格式與風格、引用的知識範圍」,把幻覺率壓低、把回答品質提升。本篇用中文 MRC 做示範,但同一套流程可以延伸到任何「問答對齊資料」:醫病對話(MedQA)、法律諮詢(LawBank)、企業客服紀錄。微調後的模型可以獨立部署,也可以接 Day 22 開始的 RAG 系統做混合架構——「微調給領域語感、RAG 給即時事實」是 2025 年中型團隊最常見的部署模式。

讀完這篇你會了解:DRCD 這類 SQuAD 格式的資料怎麼轉 Alpaca、Qwen 2.5 1.5B 在 T4 上 QLoRA 的完整超參數配置、怎麼把 Day 19 的 ROUGE-L 與 exact-match F1 接到微調後的模型、怎麼把訓練好的模型用 Ollama 部署成本地服務。本篇所有程式都可以在 Colab T4 跑完,含資料下載與訓練約 40–50 分鐘。

領域問答微調的策略與資料轉換

領域問答微調的資料有兩種主流格式。SQuAD 格式(Rajpurkar 等人 2016)是閱讀理解任務的標準:每筆樣本有 context(一段文章)、question(問題)、answers(包含答案文字與在 context 中的字元位置)。DRCD、CMRC2018、SQuAD v1/v2 都用這種格式。Alpaca 格式(Stanford Alpaca 2023)是 self-instruct 指令微調的標準:每筆有 instruction(任務描述)、input(可選的輸入)、output(期望輸出)。把 SQuAD 轉 Alpaca 的核心是把「文章 + 問題」包進 instruction、把答案放進 output,這樣模型學到的是「看著文章回答問題」的指令遵循能力。

微調策略上有三個選擇。全參數微調對 1.5B 模型在 T4 不可行(VRAM 不夠),需要 A100 等級的硬體。LoRA 微調(Day 17)只需要凍結基座、訓練 0.1% 的 LoRA 參數,T4 可以跑 1.5B–3B 模型;優點是訓練快、合併後可部署。QLoRA 微調(Day 17)把基座量化到 4 bit 再加 LoRA,VRAM 需求更低,可以在 T4 上跑 7B–13B 模型;缺點是訓練稍慢(dequantize 開銷)。本篇用 QLoRA 在 Qwen 2.5 1.5B 上示範,這是 T4 上「品質與資源」的最佳平衡點。

選擇 Qwen 2.5 1.5B 而不是 Llama 3.2 1B 的原因:Qwen 2.5 系列在中文任務上普遍優於同尺寸的 Llama(阿里在預訓練階段就用了大量中文語料)。Llama 3.2 1B 雖然有基礎中文能力,但對繁體中文的指令遵循品質稍差;對純繁中任務,Qwen 2.5 是更好的起點。如果你偏好 Llama 3.3,把模型名改成 meta-llama/Llama-3.2-1B 或 meta-llama/Llama-3.3-8B-Instruct 即可,流程不變。

完整實作:DRCD → Alpaca → QLoRA 微調 → 評估

以下範例建立一個完整的領域 QA 微調管線,從 DRCD 載入到模型合併。執行前先安裝套件:pip install peft==0.14 trl==0.15 transformers==4.49 accelerate==1.0 datasets==3.2 bitsandbytes==0.45 sentencepiece==0.2 rouge-score==0.1.2。DRCD 約 50 MB,從 GitHub 下載(wget https://github.com/DRCKnowledgeTeam/DRCD/.../DRCD_training.json),或用 Hugging Face 上的鏡像 hfl/drcd。

# 1. 載入 DRCD 並轉成 Alpaca 格式
import json
from datasets import Dataset, DatasetDict

# 從 Hugging Face 鏡像載入 DRCD(JSON 格式、約 50 MB)
# DRCD 原始釋出:https://github.com/DRCKnowledgeTeam/DRCD
# CC BY-NC-SA 4.0(研究/非商用);商用需另取得授權或換成其他公開資料集
ds = DatasetDict({
    "train": Dataset.from_list(json.loads(open("DRCD_training.json", encoding="utf-8").read())["data"]),
    "validation": Dataset.from_list(json.loads(open("DRCD_dev.json", encoding="utf-8").read())["data"]),
})
print(f"訓練集:{len(ds['train'])} 篇文章")
print(f"驗證集:{len(ds['validation'])} 篇文章")
# 輸出(範例,實際數字會略有不同):
# 訓練集:10014 篇文章
# 驗證集:1320 篇文章

這段載入 DRCD。DRCD 的格式是 SQuAD-like:每篇文章是一個 entry,裡面有多個 paragraph,每個 paragraph 有 context(段落文字)與 qas(問答題目),每個 QA 有 question、id、answers(含 text 與 answer_start)。DRCD 從維基百科與一些中文新聞取材,文章涵蓋歷史、科學、文化、社會等多個主題,是中文領域問答的標準基準之一。注意 DRCD 的授權是 CC BY-NC-SA 4.0:允許研究與非商業使用、衍生作品需相同授權,商用前要先評估授權限制。

# 2. 把 DRCD 攤平成 Alpaca 格式(每個 QA 一筆)
import re

def drcd_to_alpaca(examples):
    """把 DRCD 的 SQuAD 格式轉成 Alpaca 的 instruction/input/output。"""
    rows = []
    for article in examples["data"]:
        title = article.get("title", "")
        for para in article["paragraphs"]:
            context = para["context"]
            for qa in para["qas"]:
                if not qa["answers"]:   # 跳過無答案的(DRCD 沒有,但預留)
                    continue
                # 標準做法:用一個固定模板 + 跟引用 context
                instruction = (
                    f"請根據以下文章回答問題。如果文章中沒有答案,請回答「無法確定」。\n\n"
                    f"文章標題:{title}\n文章內容:{context}\n\n問題:{qa['question']}"
                )
                output = qa["answers"][0]["text"]   # 取第一個答案
                rows.append({"instruction": instruction, "input": "", "output": output})
    return {"instruction": [r["instruction"] for r in rows],
            "input": [r["input"] for r in rows],
            "output": [r["output"] for r in rows]}

# 攤平:train 取 3000 筆做示範(完整 34K 需約 2 小時),validation 取 200 筆
flat_train = ds["train"].select(range(min(60, len(ds["train"]))).map(
    drcd_to_alpaca, batched=True, remove_columns=ds["train"].column_names, batch_size=10
).select(range(3000))
flat_val = ds["validation"].select(range(min(30, len(ds["validation"]))).map(
    drcd_to_alpaca, batched=True, remove_columns=ds["validation"].column_names, batch_size=10
).select(range(200))

print(f"訓練樣本:{len(flat_train)} 筆")
print(f"驗證樣本:{len(flat_val)} 筆")
print("\n範例樣本:")
print("INSTRUCTION:", flat_train[0]["instruction"][:200], "...")
print("OUTPUT:", flat_train[0]["output"])
# 輸出(範例):
# 訓練樣本:3000 筆
# 驗證樣本:200 筆
# 範例樣本:
# INSTRUCTION: 請根據以下文章回答問題...
# OUTPUT: 維基百科創辦人是吉米·威爾斯

這段把 DRCD 攤平成 Alpaca 格式。drcd_to_alpaca 把每個 QA 對轉成一筆 instruction,把 context 與 question 組合成一個完整的指令,並明確指示「如果沒有答案請說無法確定」——這個提示對減少幻覺非常關鍵。範例中 instruction 包含「文章標題、文章內容、問題」三段,模型學到的是「從給定文章提取答案」這個能力。實際訓練時 3,000 筆是個甜蜜點:太少學不到、太多訓練時間拉長;1.5B 模型用 3,000 筆 + 1 epoch 在 T4 約 25 分鐘就能看出明顯效果。select(range(...)) 的雙重呼叫是 datasets 函式庫的特性:先 select 限定文章範圍再 map 攤平,最後再 select 限定樣本數。

# 3. 載入 Qwen 2.5 1.5B 並做 4 bit 量化
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig

MODEL_NAME = "Qwen/Qwen2.5-1.5B"

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)

tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token

model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME, quantization_config=bnb_config, device_map="auto",
)
model.config.use_cache = False
print(f"模型載入完成,VRAM:{torch.cuda.memory_allocated() / 1e9:.2f} GB")
# 輸出:模型載入完成,VRAM:約 1.1 GB

這段載入 Qwen 2.5 1.5B 並用 NF4 量化。Qwen 2.5 1.5B 是阿里在 2024 年 9 月發布的中英文雙語模型,對繁體中文的指令遵循品質在 1B–2B 級別是最好的選擇之一。量化後 VRAM 約 1.1 GB(bf16 要 3 GB),多出來的空間可以放更長的序列(4K tokens)或更大的 batch。device_map="auto" 讓 accelerate 自動把模型放到 GPU 0。use_cache=False 是訓練時的標準設定(Day 17 解釋過原因)。Qwen 2.5 系列使用 ChatML 格式(<|im_start|>role\n內容<|im_end|>),tokenizer 內建支援。

# 4. 配 QLoRA:用 peft 0.14 的標準寫法
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

model = prepare_model_for_kbit_training(model)

lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],   # Qwen 官方建議:注意+MLP
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 輸出:trainable params: 4,718,592 || all params: 1,562,179,072 || trainable%: 0.3020

這段配 QLoRA。prepare_model_for_kbit_training 是 QLoRA 的關鍵步驟(Day 17 解釋過):把 layernorm 與輸出層轉 fp32、凍結 base model。target_modules 列了 7 個——注意力投影(q/k/v/o)+ MLP 三層(gate/up/down)。Qwen 系列的官方建議是把 MLP 也加進 LoRA,因為 Qwen 的 MLP 在中文理解上扮演關鍵角色;如果只加注意力投影,訓練效果會差 10–15%。r=16 與 lora_alpha=32 是中等設定(alpha = 2r),對 1.5B 模型足夠。可訓練參數約 472 萬,佔總參數 0.30%——這個比例比 Llama 3.2 1B 的 0.14% 高,因為我們多加了 MLP 三層。

# 5. 訓練:用 trl 0.15 的 SFTTrainer
from trl import SFTTrainer, SFTConfig

sft_config = SFTConfig(
    output_dir="./qwen15b-drcd-lora",
    num_train_epochs=1,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,        # 等效 batch size = 16
    learning_rate=2e-4,
    warmup_ratio=0.03,
    logging_steps=20,
    save_strategy="epoch",
    bf16=True,
    max_seq_length=1024,                  # DRCD 樣本較長,給 1024
    dataset_text_field="instruction",
    packing=False,
)

trainer = SFTTrainer(
    model=model, args=sft_config,
    train_dataset=flat_train,
    eval_dataset=flat_val,
    processing_class=tokenizer,
)

trainer.train()
# 訓練約 25–35 分鐘,最終 loss 約 1.6–2.0(實際數字會略有不同)

這段是訓練迴圈。SFTTrainer(trl 0.15)會自動套上 apply_chat_template(把 Alpaca 三欄轉成 Qwen 的 ChatML 格式)、自動 padding、自動計算 loss。max_seq_length=1024 比 Day 17 的 512 長,因為 DRCD 的 instruction 含完整文章段落,平均長度約 600 字元(300 tokens 左右),上限偶爾會超過 1024,這時會自動截斷。per_device_train_batch_size=4 + gradient_accumulation_steps=4 等效 batch size 16,對 1.5B 模型是穩定的設定。learning_rate=2e-4 是 LoRA 的標準 lr(Day 17 解釋過:比全參數微調高 5–10 倍)。3,000 筆 + 1 epoch 在 T4 約 25 分鐘,最終 loss 約 1.6–2.0。

# 6. 評估:用 Day 19 的 ROUGE-L 與 exact-match F1
from rouge_score import rouge_scorer
import re

scorer = rouge_scorer.RougeScorer(["rougeL"], use_stemmer=False)

def normalize_zh(s: str) -> str:
    """標準化:去空白、統一標點,方便 exact-match。"""
    s = re.sub(r"\s+", "", s)
    s = s.replace(",", "、").replace("。", "").replace("?", "").replace("!", "")
    return s.strip()

def exact_match_f1(pred: str, gold: str) -> tuple[float, float]:
    """中文 exact-match + token-level F1。"""
    pred_norm = normalize_zh(pred)
    gold_norm = normalize_zh(gold)
    em = float(pred_norm == gold_norm)
    # token F1(以字為單位)
    pred_chars = set(pred_norm)
    gold_chars = set(gold_norm)
    overlap = pred_chars & gold_chars
    precision = len(overlap) / len(pred_chars) if pred_chars else 0
    recall = len(overlap) / len(gold_chars) if gold_chars else 0
    f1 = 2 * precision * recall / (precision + recall + 1e-9)
    return em, f1

# 在驗證集上評估(取 50 筆示範,全 200 筆約 5 分鐘)
results = []
for sample in flat_val.select(range(50)):
    # 用 Qwen 2.5 的 ChatML 格式生成回答
    messages = [{"role": "user", "content": sample["instruction"]}]
    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    inputs = tokenizer(text, return_tensors="pt").to(model.device)
    with torch.no_grad():
        out = model.generate(**inputs, max_new_tokens=128, do_sample=False,
                             pad_token_id=tokenizer.pad_token_id)
    pred = tokenizer.decode(out[0][inputs["input_ids"].shape[-1]:], skip_special_tokens=True).strip()
    em, f1 = exact_match_f1(pred, sample["output"])
    r = scorer.score(sample["output"], pred)["rougeL"].fmeasure
    results.append({"em": em, "f1": f1, "rougeL": r})

import numpy as np
print(f"驗證集 {len(results)} 筆評估:")
print(f"  Exact-Match:{np.mean([r['em'] for r in results]):.3f}")
print(f"  Token F1:  {np.mean([r['f1'] for r in results]):.3f}")
print(f"  ROUGE-L:  {np.mean([r['rougeL'] for r in results]):.3f}")
# 輸出(範例,實際數字會略有不同):
# 驗證集 50 筆評估:
#   Exact-Match:0.420
#   Token F1:   0.612
#   ROUGE-L:    0.535

這段在驗證集上做三層評估。exact_match_f1 是中文 QA 的標準指標(exact-match 嚴格比對、F1 用字元集合的 precision-recall);ROUGE-L(Day 19)是 recall 導向的字序列相似度。3,000 筆微調後的 Qwen 2.5 1.5B 在 DRCD 50 筆樣本上可以達到 EM 約 0.4、F1 約 0.6、ROUGE-L 約 0.5——這個數字對 1.5B 模型 + 3,000 筆 + 1 epoch 已經是不錯的起點;微調前(基座模型)的 EM 通常 < 0.1,因為模型沒有指令遵循能力。

# 7. 合併並存成可部署模型(用 Ollama 或 transformers 直接載入)
merged_model = model.merge_and_unload()
merged_model.save_pretrained("./qwen15b-drcd-merged", safe_serialization=True)
tokenizer.save_pretrained("./qwen15b-drcd-merged")

# 也可以輸出 GGUF 給 Ollama 用(需 llama.cpp 的 convert script)
# 指令:python llama.cpp/convert_hf_to_gguf.py ./qwen15b-drcd-merged --outfile qwen15b-drcd.gguf

# 載入合併後的模型:不需要 peft
from transformers import AutoModelForCausalLM
m = AutoModelForCausalLM.from_pretrained("./qwen15b-drcd-merged", torch_dtype=torch.bfloat16)
print(f"合併後模型參數:{sum(p.numel() for p in m.parameters()) / 1e9:.2f} B")
# 輸出:合併後模型參數:1.54 B

這段把 LoRA 權重合併回基座、存成可部署的 HF 格式。merge_and_unload 計算 W' = W + B·A、釋放 LoRA 層、回傳普通 PreTrainedModel(Day 17 解釋過原理)。合併後模型參數量 1.54 B(Qwen 2.5 1.5B + 詞彙與 embedding 的 0.04 B),與基座完全相同,但行為已經微調過。部署時兩條路:HF transformers 直接載入(適合 FastAPI、vLLM)、轉成 GGUF 後用 Ollama 載入(適合本機輕量部署)。

常見錯誤與踩雷

錯誤一:把 DRCD 當商用資料集直接用。DRCD 是 CC BY-NC-SA 4.0 授權(研究/非商業、衍生作品需相同授權),商業產品使用前要先評估授權邊界。對應排查方向:商業應用換成其他授權更寬鬆的資料集(如 Dolly-15k CC BY-SA 3.0,但品質與領域不符);或自行標註領域 QA 資料(最穩但最貴)。Day 18 提到的授權檢查流程是必做步驟。

錯誤二:instruction 模板裡把 context 放在 question 後面。我們用「文章 + 問題」的順序,這對大多數模型最自然;如果反過來寫成「問題 + 文章」,模型往往會忽略文章、憑預訓練知識亂答。對應排查方向:模板統一用「context first, question second」;做 ablation 試兩種順序,比較 F1 差距。

錯誤三:Qwen 系列的 LoRA target_modules 只加注意力層。Llama 系列的官方建議是只加 q_proj、k_proj、v_proj、o_proj,但 Qwen 系列的官方建議是加上 gate_proj、up_proj、down_proj(MLP 三層)。只加注意力會讓 Qwen 2.5 的 F1 掉 10–15%。對應排查方向:先用 Qwen 官方建議的 7 個 target_modules;如果 VRAM 緊張再考慮只加注意力。

錯誤四:max_seq_length 設太短導致 context 被截斷。DRCD 的 instruction 含完整文章,平均長度 600–800 字元;如果 max_seq_length=512,會把後半段的文章截斷掉,模型看到一半的 context 就回答,品質當然差。對應排查方向:先用 tokenizer(...).input_ids 量一下資料集的長度分佈,設 max_seq_length 為 P95 + 一些 buffer(DRCD 設 1024 足夠)。

錯誤五:評估時用 sampling 而非 greedy。do_sample=True 會讓生成結果有隨機性,每次跑分數都不同,無法重現。對應排查方向:評估階段一律用 do_sample=False(greedy decoding);需要多樣性時再用 do_sample=True 並固定 seed。本篇範例已經設 do_sample=False。

效能與實務提醒

QLoRA 微調 Qwen 2.5 1.5B 在 T4 的時間分佈:模型載入約 1 分鐘、tokenize 約 30 秒、3,000 筆訓練 1 epoch 約 25 分鐘、評估 50 筆約 3 分鐘。整個流程含下載約 40–50 分鐘。換成 Qwen 2.5 7B 訓練時間會拉到 90–120 分鐘(要確定 T4 能裝下,建議先試 3B 再上 7B)。

領域 QA 微調的關鍵超參數:learning_rate 對 LoRA 是 2e-4、QLoRA 也是 2e-4(不是 1e-4);r=16 是好的起點,領域簡單可以降到 8、領域複雜升到 32;num_train_epochs 對小資料(<5K 筆)通常 2–3 epoch 會比 1 epoch 稍好,但要監控驗證 loss 避免過擬合。per_device_train_batch_size 在 T4 + 1.5B + QLoRA 下可以試 8,視 VRAM 調整(5B 設 4、7B 設 2)。

另一個實務提醒是評估指標的選擇。中文領域 QA 場景中,exact-match(EM)與 token-level F1 是 SQuAD 時代就建立的標準;今天又多了 ROUGE-L 與 BERTScore 兩個 n-gram 與語意層級的指標可以看。一般來說,EM 最嚴格、會低估模型能力;F1 與 ROUGE-L 較寬鬆、能抓到部分對的答案;BERTScore 最接近人類判斷但成本較高。建議至少跑 EM + F1 + ROUGE-L 三項,這三項加起來幾秒鐘就能算完,能給出完整的品質輪廓;如果要對外發布評估報告,再加 BERTScore 或 LLM-as-judge。

資料量的經驗法則:1.5B 模型在領域 QA 任務上,1,000 筆可以看到初步效果、3,000 筆達到甜蜜點、10,000 筆接近飽和。換成 7B 模型,相同資料量的表現會比 1.5B 好 10–20%,但需要的資料量也更高(<3,000 筆可能還不如 1.5B + 3,000 筆)。微調 vs. RAG 的選擇:純領域問答(封閉領域、答案都在 context)用微調;開放領域(答案可能不在 context)用 RAG 或微調 + RAG 的混合架構。本篇是純領域 QA,所以微調即可;Day 25 開始會教 RAG 架構。

模型合併後的部署選項有兩條。一是 HF transformers 載入 + FastAPI 服務化(Day 40 會展開),優點是直接用 Python API、整合簡單;缺點是 throughput 受限於單機 GPU。二是轉成 GGUF 格式 + Ollama 載入,優點是輕量、支援 Apple Silicon、CPU 也能跑;缺點是要先裝 llama.cpp 的 convert 工具、量化會犧牲一點品質。實務上「研發階段用 FastAPI、production 階段用 Ollama 或 vLLM」是 2025 年中小團隊最常見的標準部署模式。

小結

今天把領域 QA 微調的工程化流程走完:我們用 DRCD(CC BY-NC-SA 4.0)做中文閱讀理解資料,攤平為 Alpaca 格式,用 QLoRA 對 Qwen 2.5 1.5B 做微調,在 50 筆驗證樣本上得到 EM 0.42、F1 0.61、ROUGE-L 0.54 的基線成績。整個流程在 50 分鐘內可跑完,包括資料下載、模型載入、訓練、評估、合併。這個工作流可以延伸到任何領域 QA 場景,只要把 DRCD 換成你領域的 QA 資料集、把 Qwen 換成你偏好的基座模型即可。明天 Day 22 我們會從「模型的客製化」轉到「語意表示的基礎」:sentence-transformers 的嵌入向量與 cosine similarity,這是 Day 23 開始的向量資料庫與 RAG 系統的基礎元件。

結語

今天的核心訊息是「領域 QA 微調是 2025 年中型團隊最務實的 LLM 客製化方案」。我們用真實的公開資料集(DRCD)、業界標準的格式(Alpaca)、可重現的微調流程(peft + trl + QLoRA)、可量化的評估(EM、F1、ROUGE-L)走完一個完整的閉環。讀完這篇你應該能回答:DRCD 的授權限制是什麼?SQuAD 格式怎麼轉 Alpaca?Qwen 與 Llama 在 LoRA target_modules 上的差異?prepare_model_for_kbit_training 在 QLoRA 扮演什麼角色?為什麼評估要用 greedy decoding?明天,我們會從「模型微調」轉到「語意表示的基礎建設」:sentence-transformers 的嵌入向量與 cosine similarity,這是 Day 23 開始的向量資料庫、Day 24 開始的語意搜尋、Day 25 開始的 RAG 系統的核心元件。

延伸資源

  • Shao 等人,2018,DRCD: A Chinese Machine Reading Comprehension Dataset(arXiv:1806.00920):https://github.com/DRCKnowledgeTeam/DRCD,本篇使用的領域問答資料集,CC BY-NC-SA 4.0 授權(研究/非商業)。
  • Cui 等人,2018,CMRC 2018: A Challenge for Machine Reading Comprehension on Chinese(arXiv:1810.00853):https://github.com/ymcui/cmrc2018,另一個中文 MRC 基準,與 DRCD 互補。
  • Stanford Alpaca 原始程式(2023):https://github.com/tatsu-lab/stanford_alpaca,Alpaca 格式定義與 self-instruct 生成流程,CC BY-NC 4.0 授權。
  • Hugging Face PEFT 官方文件(0.14,2025):https://huggingface.co/docs/peft,LoraConfig、get_peft_model 的 API。
  • Hugging Face TRL 官方文件(0.15,2025):https://huggingface.co/docs/trl,SFTTrainer 與 SFTConfig 的完整說明。
  • Qwen 2.5 官方模型卡(2024):https://huggingface.co/Qwen/Qwen2.5-1.5B,Qwen 2.5 系列的中文能力說明與建議的微調超參數。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...