- 使用公開的約 90 萬筆文言文–白話文平行語料[4],搭配 Qwen2.5-7B + QLoRA 微調,可在 Google Colab 免費 T4 GPU 上完成訓練
- QLoRA 只更新不到 1% 的參數,記憶體需求從 28 GB 壓縮到約 8 GB,完全塞得進 T4 的 16 GB VRAM
- 微調後的模型能將「項莊舞劍,意在沛公」流暢翻譯成「項莊表面上是在舞劍助興,實際上想刺殺劉邦」
- 2026 年的當下,前沿大模型(GPT-4o、Claude、Qwen2.5-72B)已能零樣本翻譯大部分文言文,RAG + 典籍辭典的方案在專業場景更有優勢
一、為什麼要微調一個「文言文翻譯機」?
先承認一件事:2026 年的大型語言模型,直接丟一段史記給它,多半都能翻得八九不離十。那我們為什麼還要自己微調?
三個理由:
- 學習 LLM 微調的最佳練習場——文言文翻白話文是一個完美的 seq2seq 任務:輸入明確、輸出明確、有大量平行語料、結果好不好一眼就看得出來
- 成本控制——如果你要大量翻譯古籍(例如建一個古文數位典藏平台),每次都呼叫 GPT-4o API 是很貴的。微調一個 7B 模型,自己部署,長期成本低得多
- 可控性——你可以決定翻譯風格(信達雅的比例)、處理特殊術語的方式、輸出格式等,這些是通用大模型難以精確控制的
而司馬遷的《史記》[10]是我們今天的練功場——全書一百三十篇、五十二萬字,涵蓋帝王本紀、諸侯世家、名人列傳,文體多樣、難度適中,是測試翻譯品質的理想材料。
二、整體流程概覽
先看大圖再動手:
- 取得平行語料:從 Hugging Face 載入約 90 萬筆文言文↔白話文的句對
- 選擇基座模型:Qwen2.5-7B-Instruct(目前中文表現最佳的開源 7B 級模型之一)
- 格式化訓練資料:轉成 instruction-tuning 格式(「請將以下文言文翻譯成白話文:⋯⋯」)
- QLoRA 微調:4-bit 量化 + LoRA adapter,在 T4 GPU 上跑得動
- 測試翻譯效果:拿史記原文來試試
三、在 Google Colab 上動手做
打開 Google Colab,執行階段切換到 T4 GPU(必須,CPU 跑不動微調)。
3.1 安裝依賴
我們使用 Unsloth[6]——它能讓微調速度加快 2 倍、記憶體省 60%,而且跟 Hugging Face 生態完全相容:
# 安裝 Unsloth(已整合 bitsandbytes、peft、trl 等)
!pip install unsloth -q
!pip install datasets -q
import torch
from unsloth import FastLanguageModel
print(f"PyTorch: {torch.__version__}")
print(f"CUDA available: {torch.cuda.is_available()}")
if torch.cuda.is_available():
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"VRAM: {torch.cuda.get_device_properties(0).total_mem / 1024**3:.1f} GB")
3.2 載入基座模型(4-bit 量化)
# ★ 載入 Qwen2.5-7B-Instruct,4-bit 量化 ★
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Qwen2.5-7B-Instruct-bnb-4bit",
max_seq_length=2048,
dtype=None, # 自動偵測
load_in_4bit=True, # QLoRA 的關鍵:4-bit 量化
)
print(f"模型載入完成!參數量: {model.num_parameters():,}")
Qwen2.5-7B-Instruct[3] 是阿里巴巴通義千問團隊的開源模型,中文理解能力在 7B 級距中屬於頂尖。4-bit 量化後大約只佔 4–5 GB VRAM,留給訓練足夠的空間。
3.3 添加 LoRA Adapter
# ★ 設定 LoRA adapter ★
model = FastLanguageModel.get_peft_model(
model,
r=16, # LoRA rank(越大越有表達力,但越吃記憶體)
target_modules=[ # 要微調的模組
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],
lora_alpha=16,
lora_dropout=0, # Unsloth 建議設 0(已內建 regularization)
bias="none",
use_gradient_checkpointing="unsloth", # 省 30% 記憶體
random_state=42,
)
# 看看可訓練參數量
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
total = sum(p.numel() for p in model.parameters())
print(f"可訓練參數: {trainable:,} / {total:,} ({trainable/total*100:.2f}%)")
LoRA[2] 的核心概念:不動原本的 7B 參數,而是在特定層旁邊插入一個小小的「旁路」(低秩矩陣),只訓練這個旁路。通常可訓練參數不到原模型的 1%——但效果幾乎跟全量微調一樣好[1]。
3.4 載入文言文–白話文平行語料
# ★ 載入公開的文言文-白話文平行語料 ★
from datasets import load_dataset
# raynardj 的 wenyanwen 語料:約 90 萬筆文言文-白話文句對
dataset = load_dataset(
"raynardj/wenyanwen-ancient-translate-to-modern",
split="train"
)
print(f"總筆數: {len(dataset):,}")
print(f"欄位: {dataset.column_names}")
# 看幾筆範例
for i in range(3):
print(f"\n--- 範例 {i+1} ---")
print(f"文言文: {dataset[i]['ancient']}")
print(f"白話文: {dataset[i]['modern']}")
這個資料集[4]收錄了大量經典古籍的平行翻譯,包含史記、左傳、資治通鑑、論語等。每一筆都是一個文言文句子對應一個白話文翻譯。
3.5 格式化成 Instruction-Tuning 資料
# ★ 轉換成 Qwen 的 ChatML 格式 ★
SYSTEM_PROMPT = "你是一位精通古典文學的翻譯專家。請將使用者提供的文言文準確翻譯成流暢的現代白話文,保留原文的意思和語氣。"
def format_conversation(example):
"""將平行語料轉成 ChatML instruction-tuning 格式"""
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"請將以下文言文翻譯成白話文:\n\n{example['ancient']}"},
{"role": "assistant", "content": example['modern']},
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False
)
return {"text": text}
# 取子集訓練(全量 90 萬筆在 Colab 上要跑很久)
# 建議:先用 10,000 筆試跑,確認沒問題後再加大
TRAIN_SIZE = 10000
dataset_subset = dataset.shuffle(seed=42).select(range(TRAIN_SIZE))
formatted_dataset = dataset_subset.map(format_conversation)
print(f"訓練集大小: {len(formatted_dataset)}")
print(f"\n格式化範例(前 500 字元):\n{formatted_dataset[0]['text'][:500]}")
3.6 開始微調
# ★ 設定 SFTTrainer 開始微調 ★
from trl import SFTTrainer
from transformers import TrainingArguments
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=formatted_dataset,
dataset_text_field="text",
max_seq_length=2048,
dataset_num_proc=2,
packing=True, # 把短句子打包在一起,提高 GPU 利用率
args=TrainingArguments(
per_device_train_batch_size=2,
gradient_accumulation_steps=4, # 等效 batch_size = 8
warmup_steps=50,
num_train_epochs=3,
learning_rate=2e-4,
fp16=not torch.cuda.is_bf16_supported(),
bf16=torch.cuda.is_bf16_supported(),
logging_steps=25,
optim="adamw_8bit",
weight_decay=0.01,
lr_scheduler_type="linear",
seed=42,
output_dir="outputs",
report_to="none",
),
)
# 開始訓練!
print("開始微調...")
stats = trainer.train()
print(f"\n訓練完成!")
print(f" 總步數: {stats.global_step}")
print(f" 訓練時間: {stats.metrics['train_runtime']:.0f} 秒")
print(f" 最終 loss: {stats.metrics['train_loss']:.4f}")
在 T4 GPU 上,10,000 筆資料、3 個 epoch 大約需要 20–40 分鐘。你會看到 loss 從一開始的 2.x 逐漸降到 1.x 甚至更低——這代表模型越來越會翻譯了。
四、測試翻譯效果
訓練完了,來拿史記的原文測試:
# ★ 測試:翻譯史記名段 ★
FastLanguageModel.for_inference(model)
test_passages = [
# 史記·項羽本紀
"項莊舞劍,意在沛公。",
# 史記·陳涉世家
"燕雀安知鴻鵠之志哉!",
# 史記·廉頗藺相如列傳
"完璧歸趙。",
# 史記·太史公自序
"究天人之際,通古今之變,成一家之言。",
# 史記·刺客列傳
"風蕭蕭兮易水寒,壯士一去兮不復還。",
# 較長段落:史記·項羽本紀·鴻門宴
"沛公旦日從百餘騎來見項王,至鴻門,謝曰:臣與將軍戮力而攻秦,將軍戰河北,臣戰河南,然不自意能先入關破秦,得復見將軍於此。今者有小人之言,令將軍與臣有郤。",
]
def translate(text):
"""用微調後的模型翻譯文言文"""
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"請將以下文言文翻譯成白話文:\n\n{text}"},
]
inputs = tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_tensors="pt"
).to("cuda")
outputs = model.generate(
input_ids=inputs,
max_new_tokens=512,
temperature=0.3, # 低溫度:更忠實於原文
top_p=0.9,
repetition_penalty=1.1,
)
# 只取生成的部分
response = tokenizer.decode(outputs[0][inputs.shape[-1]:], skip_special_tokens=True)
return response.strip()
# 逐段翻譯
for passage in test_passages:
print(f"【文言文】{passage}")
translation = translate(passage)
print(f"【白話文】{translation}")
print("-" * 60)
你應該會看到類似這樣的翻譯結果:
【文言文】燕雀安知鴻鵠之志哉!
【白話文】燕子和麻雀怎麼能夠知道天鵝的志向呢!
【文言文】究天人之際,通古今之變,成一家之言。
【白話文】探究天道與人事之間的關係,通曉從古到今的歷史變化,形成自己獨到的見解。
4.1 跟原始模型做對照
# 對照實驗:看看微調前後的差異
# 載入原始模型(不帶 adapter)
from peft import PeftModel
# 暫時停用 adapter
model.disable_adapter_layers()
print("=== 原始 Qwen2.5-7B(未微調)===")
test_text = "項莊舞劍,意在沛公。"
print(f"文言文: {test_text}")
print(f"翻譯: {translate(test_text)}")
# 重新啟用 adapter
model.enable_adapter_layers()
print(f"\n=== 微調後 ===")
print(f"文言文: {test_text}")
print(f"翻譯: {translate(test_text)}")
微調後的模型通常在翻譯的「信」和「達」上都會有明顯提升——用詞更準確、句子更通順,特別是在處理典故和專有名詞時。
五、儲存模型
# ★ 儲存 LoRA adapter ★
model.save_pretrained("wenyanwen-translator-lora")
tokenizer.save_pretrained("wenyanwen-translator-lora")
print("LoRA adapter 已儲存!")
# 如果要上傳到 Hugging Face Hub:
# model.push_to_hub("your-username/wenyanwen-translator-lora")
# tokenizer.push_to_hub("your-username/wenyanwen-translator-lora")
# 如果要合併成完整模型(方便部署):
# model.save_pretrained_merged("wenyanwen-translator-merged", tokenizer)
LoRA adapter 通常只有幾十 MB——比起完整的 7B 模型(14 GB),小得可以忽略。你可以輕鬆下載到本機或上傳到 Hugging Face Hub。
六、完整程式碼(一鍵複製版)
# === LLM 微調文言文翻譯 完整版 ===
# 環境:Google Colab + T4 GPU
# Step 1: 安裝
!pip install unsloth datasets -q
# Step 2: 載入模型(4-bit 量化)
import torch
from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Qwen2.5-7B-Instruct-bnb-4bit",
max_seq_length=2048,
dtype=None,
load_in_4bit=True,
)
# Step 3: 添加 LoRA
model = FastLanguageModel.get_peft_model(
model, r=16,
target_modules=["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"],
lora_alpha=16, lora_dropout=0, bias="none",
use_gradient_checkpointing="unsloth",
)
# Step 4: 載入平行語料
from datasets import load_dataset
dataset = load_dataset("raynardj/wenyanwen-ancient-translate-to-modern", split="train")
SYSTEM_PROMPT = "你是一位精通古典文學的翻譯專家。請將使用者提供的文言文準確翻譯成流暢的現代白話文,保留原文的意思和語氣。"
def format_conversation(example):
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"請將以下文言文翻譯成白話文:\n\n{example['ancient']}"},
{"role": "assistant", "content": example['modern']},
]
return {"text": tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)}
formatted = dataset.shuffle(seed=42).select(range(10000)).map(format_conversation)
# Step 5: 微調
from trl import SFTTrainer
from transformers import TrainingArguments
trainer = SFTTrainer(
model=model, tokenizer=tokenizer,
train_dataset=formatted,
dataset_text_field="text",
max_seq_length=2048, packing=True,
args=TrainingArguments(
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
warmup_steps=50, num_train_epochs=3,
learning_rate=2e-4,
fp16=not torch.cuda.is_bf16_supported(),
bf16=torch.cuda.is_bf16_supported(),
logging_steps=25, optim="adamw_8bit",
output_dir="outputs", report_to="none",
),
)
trainer.train()
# Step 6: 測試
FastLanguageModel.for_inference(model)
def translate(text):
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"請將以下文言文翻譯成白話文:\n\n{text}"},
]
inputs = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to("cuda")
outputs = model.generate(input_ids=inputs, max_new_tokens=512, temperature=0.3)
return tokenizer.decode(outputs[0][inputs.shape[-1]:], skip_special_tokens=True).strip()
print(translate("燕雀安知鴻鵠之志哉!"))
# Step 7: 儲存
model.save_pretrained("wenyanwen-translator-lora")
tokenizer.save_pretrained("wenyanwen-translator-lora")
七、拆解黑箱:微調到底改了什麼?
你可能好奇:LoRA 只動了不到 1% 的參數,為什麼翻譯品質就能提升這麼多?
這要從 LLM 的「知識」和「能力」說起:
- 知識(Knowledge):存在模型的 FFN 層中——Qwen2.5 預訓練時已經讀過大量中文古典文獻,它「知道」文言文的語法和詞彙
- 能力(Capability):存在 Attention 層的投影矩陣中——模型「知道」但不一定「擅長」特定格式的輸出
LoRA 微調[2]做的事情,本質上是校準模型的注意力模式:讓它在看到「請翻譯文言文」的指令時,知道要把注意力集中在文言文的語法結構、虛詞用法、古今詞義差異上,然後輸出通順的白話文。模型本來就有這個能力,微調只是把它「啟動」了。
QLoRA[1] 更進一步:把基座模型壓縮到 4-bit(原本每個參數用 16-bit 儲存),但 LoRA adapter 本身還是用 16-bit 訓練。這樣既省記憶體,又不犧牲微調精度。
八、2026 年的當下,有更好的方法嗎?
8.1 零樣本翻譯:直接問大模型
老實說,2026 年的前沿大模型已經很強了。如果你的需求是「偶爾翻譯幾段古文」,直接問就好:
# 用 Qwen2.5-72B(或 GPT-4o、Claude)直接翻譯
# 這裡示範用 Hugging Face Inference API
!pip install huggingface_hub -q
from huggingface_hub import InferenceClient
client = InferenceClient("Qwen/Qwen2.5-72B-Instruct")
response = client.chat_completion(
messages=[
{"role": "system", "content": "你是文言文翻譯專家,請準確翻譯為白話文。"},
{"role": "user", "content": "請翻譯:沛公旦日從百餘騎來見項王,至鴻門,謝曰:臣與將軍戮力而攻秦,將軍戰河北,臣戰河南。"}
],
max_tokens=512,
temperature=0.3,
)
print(response.choices[0].message.content)
72B 級的模型翻譯品質通常非常好——但每次推論都要付 API 費用,而且你無法控制翻譯風格。
8.2 RAG + 古典辭典:專業場景的最佳方案
如果你要翻譯的是充滿典故、通假字、專有名詞的高難度古文,單靠 LLM 可能會出錯。TongGu[7] 提出的 CCU-RAG 方案是更好的選擇:
# RAG 增強翻譯的概念示意
# 1. 建立古典辭典向量資料庫
# 2. 翻譯前先檢索相關條目
# 3. 將檢索結果作為上下文提供給 LLM
"""
概念流程:
輸入: "完璧歸趙"
Step 1 - RAG 檢索:
→ 查到「完璧歸趙」典故出處:史記·廉頗藺相如列傳
→ 查到「璧」= 和氏璧(一種珍貴的玉器)
→ 查到「趙」= 趙國
Step 2 - 增強 Prompt:
"參考以下背景知識:
- 完璧歸趙:出自《史記·廉頗藺相如列傳》
- 璧:指和氏璧
- 趙:戰國時期的趙國
請翻譯:完璧歸趙"
Step 3 - LLM 翻譯:
→ "將完好無缺的和氏璧歸還給趙國。
比喻將物品完好地歸還給原主。"
"""
print("RAG 增強翻譯能將典故準確率提升 7 個百分點(TongGu, 2024)")
print("多義詞解析準確率提升 24 個百分點")
TongGu 的實驗顯示,RAG 增強能將典故翻譯準確率提升 7 個百分點、多義詞解析提升 24 個百分點[7]。如果你的目標是學術級的翻譯品質,這是目前的最佳路線。
8.3 多代理人協作翻譯:最新研究方向
Zhang et al. 在 2025 年發表的研究[8]提出了一個有趣的做法:用多個 LLM Agent 協作翻譯,每個 Agent 負責不同的任務——
- Agent 1(字詞專家):逐字解釋文言文的詞義
- Agent 2(翻譯者):根據詞義生成段落級白話文
- Agent 3(審校者):從信、達、雅三個維度審查翻譯品質,提出修改建議
這種分工模式讓翻譯品質顯著超越單一模型。在實務上,你可以用 LangChain 或 LangGraph 來實現這個多 Agent 架構。
8.4 WenyanGPT:專為古文打造的大模型
如果你不想自己微調,WenyanGPT[5] 是一個已經微調好的專用模型,專攻古典中文的斷句和翻譯。它在翻譯任務上的 BLEU-1 分數達到 0.47,超過第二名 0.06 個百分點。值得關注的是,它對古文斷句(為沒有標點符號的原始古文加標點)也有很好的表現——這是翻譯前很重要的預處理步驟。
8.5 方案選擇一覽
| 方案 | 適合場景 | 翻譯品質 | 成本 | 可控性 |
|---|---|---|---|---|
| QLoRA 微調 7B | 大量翻譯、自建平台 | 高 | 低(自部署) | 最高 |
| 零樣本 72B API | 偶爾翻譯、快速驗證 | 高 | 中(按量付費) | 低 |
| RAG + 辭典 | 學術研究、高難度古文 | 最高 | 中高 | 高 |
| 多 Agent 協作 | 出版級翻譯品質 | 最高 | 高(多次推論) | 高 |
| WenyanGPT | 開箱即用、斷句 + 翻譯 | 高 | 低 | 中 |
九、進階調校建議
如果你跑出了基本結果,想要更好的翻譯品質,可以試試以下調整:
- 增加訓練資料量:從 10,000 筆加到 50,000 甚至 100,000 筆。語料品質比數量重要,但在這個量級內,多就是好
- 過濾史記專屬語料:如果你特別想翻譯史記,可以從 中國哲學書電子化計劃 (ctext.org)[9] 取得史記全文,再搭配白話文譯本做對齊
- 調整 LoRA rank:
r=16是一個好的起點。如果翻譯太「死板」,可以試r=32或r=64(但記憶體需求會增加) - 多輪對話微調:加入「先解釋關鍵字詞,再翻譯全文」的多輪對話格式,讓模型學會先理解再翻譯
- 評估指標:除了人工看,也可以用 BLEU、ROUGE 等自動指標做量化評估。但中文翻譯的評估一直是個難題——同一句話可以有很多種正確的翻譯方式
十、常見問題 FAQ
Q:Colab 免費版的 T4 GPU 夠用嗎?
夠。Qwen2.5-7B 4-bit 量化約佔 4–5 GB,LoRA 訓練額外需要 3–4 GB,總共約 8 GB,T4 的 16 GB VRAM 綽綽有餘。但 Colab 免費版的 GPU 時間有限,建議一次跑完別中斷。
Q:為什麼選 Qwen2.5 而不是 LLaMA 3?
因為中文。Qwen2.5 的預訓練語料中有大量高品質中文(包含古典文獻),中文理解能力遠勝 LLaMA 3[3]。其他好的選擇還有 Yi-1.5、ChatGLM、Baichuan、DeepSeek。
Q:訓練資料只有 10,000 筆夠嗎?
作為 demo 夠了。但如果你要真正好的翻譯品質,建議用 50,000 筆以上。90 萬筆全用的話,在 T4 上大約要跑 6–8 小時——可以考慮用 Colab Pro 或者半夜掛著跑。
Q:微調後的模型可以處理史記以外的文言文嗎?
可以。因為訓練語料來自多部古籍(不只史記),所以微調後的模型對大部分文言文都有不錯的翻譯能力。但如果遇到特別冷門的典故或通假字,可能還是會出錯——這時候 RAG + 辭典的方案就派上用場了[7]。
Q:能不能反過來,把白話文翻譯成文言文?
可以。raynardj 同時也提供了白話文→文言文的語料和模型[4]。用同樣的流程,把 ancient 和 modern 欄位對調就好。不過白話文翻文言文的難度更高——畢竟,模仿司馬遷的文筆可不是一件容易的事。
十一、結語:讓兩千年前的文字活過來
司馬遷在《報任安書》裡說自己寫史記的目的是「究天人之際,通古今之變,成一家之言」[10]。兩千年後的今天,我們用 AI 來翻譯他的文字,某種意義上也是在「通古今之變」——用現代的語言,把古人的智慧傳遞給更多人。
技術層面的收穫也不少:你在這篇教學裡學到了 QLoRA 微調的完整流程、instruction-tuning 的資料格式、Unsloth 的加速技巧,以及如何在免費的 Colab T4 上跑完整個訓練。這些技能可以直接遷移到任何其他的 LLM 微調任務——客服對話、程式碼生成、醫療問答、法律摘要,流程都是一樣的。
打開 Colab,把程式碼貼進去,讓 AI 試著讀懂司馬遷吧。第一次看到模型把「項莊舞劍,意在沛公」翻成流暢的白話文時,你大概會跟我一樣覺得:這真的滿酷的。



