中文維基百科語料處理工具,每月從官方 dump 生成可直接訓練的資料集。
一次解析,三種形態:純文字給語言模型、圖文交錯給多模態、圖文配對給圖像理解。
三者出自同一次解析、同一套清理規則,可以用 id 互相對照。
| 資料集 | 形態 | 規模 |
|---|---|---|
| 純文字 · 台灣正體 🇹🇼 | 149 萬篇 | |
| 純文字 · 簡體 🇨🇳 | 149 萬篇 | |
| 圖文交錯 · 台灣正體 🖼️📝 | 149 萬篇 · 142 萬張圖 | |
| 圖文交錯 · 簡體 🖼️📝 | 149 萬篇 · 142 萬張圖 | |
| 圖文配對 🖼️ | 92.1 萬列 × 繁簡 |
同一批條目、同一套清理規則,差別只在圖片的處理方式:
| 形態 | 路徑 | 圖片怎麼處理 | 用途 |
|---|---|---|---|
| 純文字 | output/{lang}/*.parquet |
整行移除,不含圖說 | 語言模型預訓練 |
| 圖文交錯 | output/{lang}/omni/*.parquet |
換成 <image> 佔位符 + images 陣列 |
多模態預訓練 |
| 圖文配對 | output/{lang}/wiki_images_dataset*.jsonl |
一圖一列,附圖說與所在章節 | 圖像描述、檢索 |
{
"id": "100",
"title": "農業",
"url": "https://zh.wikipedia.org/wiki/农业",
"text": "農業\n\n農業屬於第一級產業,包括作物種植、畜牧、漁業養殖…\n\n## 定義\n\n根據東漢時期《說文解字》…"
}| 欄位 | 說明 |
|---|---|
id |
維基百科條目 ID |
title |
條目標題 |
url |
條目原文網址。用未經繁簡轉換的原始標題組成,確保直接命中而非 301 轉址 |
text |
條目全文。第一行為標題,章節以 Markdown ## 標示,列表為 - ,表格列以 | 分隔,程式碼用 ``` 圍欄,公式保留 LaTeX 並以 $ 包住 |
<image> 出現在圖片原本的位置,images 陣列依序對應——格式對齊
MMC4/OBELICS 的慣例:
{"id": "13", "title": "數學",
"text": "數學是研究數量、結構…\n\n<image>\n\n## 歷史\n\n數學的歷史相當悠久…",
"images": [{"url": "https://…/Euclid.jpg", "file_name": "Euclid.jpg",
"caption": "歐幾里得,古希臘數學家", "alt": ""}]}不變量:text.count("<image>") == len(images)。章節被丟棄時它的圖片一併丟棄,
兩者永遠對得上(全量實測 0 誤差)。
純文字版與 omni 版的差別只有這一點:純文字版把標記整行移除,並且順手清掉
「拿掉圖片後就空掉」的章節與表格儲存格;omni 版保留它們——政府 的「地圖」
一節只有兩張世界地圖,對純文字模型是空標題,對多模態模型是有價值的圖文對。
{
"url": "https://zh.wikipedia.org/wiki/Special:FilePath/Euclid.jpg",
"file_name": "Euclid.jpg",
"caption": "歐幾里得,西元前三世紀的古希臘數學家,此畫為拉斐爾的作品《雅典學院》",
"alt": "",
"page": "數學",
"page_id": "18",
"section": "歷史",
"page_url": "https://zh.wikipedia.org/wiki/数学"
}| 欄位 | 說明 |
|---|---|
url |
圖片檔案網址(Special:FilePath,會轉址到實際檔案) |
file_name |
原始檔名 |
caption |
圖說。與正文走同一套模板展開、殘留標記清理與繁簡轉換 |
alt |
無障礙替代文字(alt=),約 2% 的圖片有,與圖說是不同的描述 |
page / page_id |
圖片出現的條目與其 ID |
page_url |
條目網址。用未經轉換的原始標題組成,CC BY-SA 署名依據 |
section |
圖片出現在哪一節(前言為空字串),與正文的 ## 標題可直接對應 |
一次使用一列:同一張圖出現在不同條目、配著不同圖說,正是圖文配對最有價值的
部分,因此不做全域去重。要一圖一列的人可自行以 url 去重。沒有圖說也沒有 alt
的純裝飾圖片會略過,不拿檔名充當描述。
唯一會去掉的是同一條目裡整列完全相同的重複(圖、圖說、alt、章節四者都一樣)。
這種列來自原始碼本身:路牌、國旗之類的小圖示會在同一節裡逐條重出,內湖區 的
TWHW1.svg 就在「道路」節出現兩次、圖說都是「中華民國國道」。那些列一個字都沒
多帶,留著只會讓裝飾性圖示在訓練時被過度加權。圖說或章節有任何不同就保留——
寒山寺 那張圖在「寒山寺」節與「佛教」節各配一句不同的說明,那是兩個語境。
text = load_dataset("yuhuanstudio/wikipedia-zh-tw", split="train")
omni = load_dataset("yuhuanstudio/wikipedia-omni-zh-tw", split="train")
imgs = load_dataset("yuhuanstudio/wikipedia-image-zh-tw", split="train")
by_id = {r["id"]: r for r in text}
for img in imgs: # 圖片的 page_id 就是正文的 id
article = by_id.get(img["page_id"])
# article["text"] 裡的 `## {img["section"]}` 就是這張圖所在的段落圖說只在圖片與 omni 資料集裡,不進純文字正文:它說的是「這張圖是什麼」, 而正文要的是條目本身的敘述。所以三份資料互補而非重複。
⚠️ 圖片授權:維基圖片的授權各不相同(CC BY-SA、公有領域,也有合理使用 的非自由圖片如商標、專輯封面)。授權資訊不在pages-articlesdump 裡,本資料集 不含授權欄位,使用前請自行向 Commons/維基百科查證個別檔案。caption文字 來自維基百科採 CC BY-SA,請保留page_url以符合署名要求。
以 2026-10-01 的 dump 實測(繁體版;簡體版數字相近、條目 ID 序列一致):
| 規模 | |
|---|---|
| 條目數 | 1,490,712(純文字/omni/繁/簡四組完全相同) |
| 總字元 | 17.8 億 |
| Parquet 分片 | 7 個/組,zstd 壓縮 |
| 圖片列數 | 繁體 931,484;簡體 931,480 |
| omni 圖片總數 | 繁體 1,438,406;簡體 1,438,407 |
| 含圖片的條目 | 328,822(22.1%,繁簡相同),有圖者平均 4.4 張 |
條目長度分布(字元)
| p5 | p25 | p50 | p75 | p95 | p99 | max |
|---|---|---|---|---|---|---|
| 87 | 211 | 461 | 1,081 | 4,567 | 11,819 | 232,667 |
中位數 461 字反映中文維基的真實樣貌——大量物種、小行星、村里、地理特徵的短條目。 沒有長度過濾,要篩隨時可以自己做。
內容組成
| 平均章節數 | 3.5 |
| 無章節(只有前言) | 233,174(15.6%) |
| 列表為主的條目 | 317,258(21.3%) |
| 含 LaTeX 公式的條目 | 16,700 |
| 完全重複的文檔 | 0 |
| 第一行為條目標題 | 100.00% |
結尾沒有句讀的條目佔 65.2%,逐類分解後不是缺陷:列表項 92.4%、表格列 3.6%、 英數/程式碼 1.9%、極短行 1.1%,真正「散文但沒句號」只有 1.0%——列表型與年表型 條目保留原始形態,不強行改寫成散文。
離線對照表(掃一次 Template 命名空間建立,可重複使用)
| 表 | 筆數 |
|---|---|
| 無參數模板 | 47,284(含「含參數模板的固定前綴」) |
| 維護模板(訊息方塊) | 3,910 |
| 國家名稱對照 | 6,341 |
| 資訊框靜態標籤 | 14,786 |
| 資訊框動態標籤 | 1,876 |
| 參數別名推導 | 1,921 |
| 各模板專屬標籤 | 1,379 個模板 |
| 渲染反推標籤 | 473(2026-08 建立,沿用) |
| Wikidata 數值 | 41,021 篇 |
標「約」的數字是程式化估計(抽樣或啟發式),其餘為全量實測。
整條處理流程重寫過。 這不是單純的月度刷新——資料的形狀變了。
若你正在用 2607 或更早的版本,升級前請先看這張表:
2607 以前 |
2608 起 |
|
|---|---|---|
| 筆數/單位 | 371 萬列(段落級) | 148 萬篇(文檔級,一條目一筆) |
| 圖片資料集 | 54.8 萬列(按檔名全域去重) | 91.5 萬列(保留每個使用語境,繁簡各一份) |
| 檔案格式 | JSON 分段(wiki_pretrain_part*.json) |
Parquet 分片 |
| 章節結構 | 切碎後不存在 | 保留 ## 階層,可直接定位到節 |
| 形態 | 純文字 + 圖文配對 | 再加圖文交錯(omni),三者可用 id 互相對照 |
| 表格 | 整塊丟棄 | 轉成文字(表格佔可見內容約 15%) |
| 程式碼 | 散文規則套上去,縮排與換行被破壞 | 逐字保留,``` 圍欄,連全形空格都不動 |
| 公式 | 丟棄 | 保留 LaTeX,以 $ 包住 |
| 資訊框 | 整塊丟棄 | 事實抽成 ## 基本資料,欄位名取自維基的模板定義 |
| 繁簡轉換 | OpenCC s2twp |
維基官方轉換表 + 台灣慣用詞白名單 |
| 排版空格 | 有(pangu) | 移除——不可逆的有損轉換 |
| 長度過濾 | 有 | 沒有——一句話的條目也是完整知識 |
| 圖片抓取 | 只認一種前綴、不看 <gallery>,漏約 1/5 |
括號配對抓全,新增 section 定位到章節 |
| 驗證 | 殘留字元檢查 | 分層驗證:黃金案例、壓力測試、逐字比對、繁簡對等、線上對照 |
資料集的核心指標是「原文的東西丟了多少」,不是「殘留多少標記」——殘留可以 靠把整段刪掉變成 0,那不是乾淨,是把資料丟了。
做法:掃一次 dump,對每篇條目從原始碼取出可驗證的內容項目,回頭去兩個版本的 正文裡找。只比兩版都收錄的 1,367,033 篇(否則收錄率會混進保留率),比對前兩邊 都正規化成簡體並去空白(繁簡與排版差異不算內容缺失)。
| 項目 | 樣本數 | 2607 留住 |
2608 留住 |
差距 |
|---|---|---|---|---|
| 公式 | 91,552 | 0.0% | 99.2% | +99.1 pp |
| 程式碼區塊 | 6,261 | 2.9% | 99.0% | +96.1 pp |
| 表格儲存格 | 2,563,309 | 2.5% | 51.4% | +48.8 pp |
| 清單項 | 5,522,474 | 30.9% | 70.2% | +39.3 pp |
| 資訊框欄位值 | 6,721,977 | 5.1% | 30.7% | +25.6 pp |
| 段落句子 | 5,564,852 | 59.0% | 74.8% | +15.9 pp |
| 合計 | 20,470,425 | 26.4% | 56.3% | +29.9 pp |
最值得注意的是段落句子:連純散文,2607 也只留住 59%——四成的正文句子不見了。
那不只是「沒收表格」,是舊版的 normalize_text 會整句刪除(含公式、&、
特定詞的句子),見 legacy/README.md 的實例。
2608 的 56.3% 不是滿分,剩下的有明確歸屬,不是不明損失:
- 資訊框欄位值 30.7%——探針取自原始碼的所有參數,其中大量是版面設定 (圖片檔名、尺寸、色碼、對齊),那些本來就不該進正文
- 表格 51.4%——探針含樣式屬性與圖片語法;
table_audit用人工挑過的探針量, 同一份資料是 92.3% OK - 段落 74.8%——參考資料、外部連結、參見等章節依設計整節不收
- 公式 99.2%/程式碼 99.0%——這兩項的探針最乾淨,可視為保留率的上界參考
條目收錄:2607 1,419,539 篇 → 2608 1,482,182 篇(+62,643)。
把公開的 2607 資料整份抓下來(六個分段檔 2.9 GB),與 2608 的全部 148 萬篇
套完全相同的判準逐筆掃描:
2607 以前 |
2608 起 |
||
|---|---|---|---|
| 筆數 | 3,711,316 列 | 1,482,182 篇 | 單位從段落改成整篇 |
| 總字元 | 9.45 億 | 17.56 億 | ×1.86 |
| 每筆平均字元 | 255 | 1,185 | |
| 中位數字元 | 138 | 457 |
同一份維基百科,新版多出 8.1 億字元——多的不是重複,是原本被丟掉的表格、 資訊框事實、清單、程式碼與公式。
內容特徵的出現率(2607 幾乎全 0):
| 特徵 | 2607 以前 |
2608 起 |
|---|---|---|
| 章節標題 | 0.00% | 84.21% |
| 清單項 | 0.00% | 72.92% |
資訊框事實(## 基本資料) |
0.01% | 53.26% |
| 表格列 | 0.02% | 16.23% |
| LaTeX 公式 | 0.00% | 1.12% |
| 程式碼圍欄 | 0.00% | 0.11% |
殘留標記與排版缺陷(每百萬字元的出現次數,已挖掉逐字區塊):
2607 以前 |
2608 起 |
||
|---|---|---|---|
| 排版空格(pangu) | 36,851.26 | 1,276.03 | −96.5% |
| 私有區字元 | 0.38 | 0.00 | −99.8% |
| 表格空儲存格 | 0.03 | 0.00 | 清零 |
| 行尾空白 | 0.00 | 0.00 | 兩版皆 0 |
殘留模板 {{ }} |
0.21 | 0.25 | +17.6% |
| 殘留 wiki 連結 | 0.00 | 0.36 | 新增 |
| 殘留 HTML/ref/實體/連續空行 | 0.00 | ≤0.11 | 新增 |
「新增」那幾項要說清楚,不能含糊帶過:2607 之所以是 0,是因為它把含有
這些標記的內容整批刪掉了——沒有表格、沒有資訊框、沒有程式碼,自然沒有殘留。
2608 的那 0.25/0.36 已經逐筆判讀來源語意(見〈品質如何驗證〉):
<nowiki> 字面值(維基自己也顯示成那樣)、真實內容長得像標記
({{1,2,3}} 集合論、A<B> a; C++ 模板語法)、以及原文自己沒關標籤。
⚠️ 這張表的比較方法有個陷阱:若不先挖掉程式碼與公式,會算出「殘留模板 暴增 2,484%」的相反結論——因為2608保留了 LaTeX 的巢狀括號與集合論記號。2607的乾淨是靠丟掉內容換來的。
筆數從 371 萬「列」變成 148 萬「篇」不是資料變少——是切分單位從段落改成整篇。
段落級會切斷長距離依賴,也不適合現在的長 context 訓練;要段落的人可自行以
\n\n 切,反過來救不回來。
圖片資料集(兩版全量逐列比對):
2607 以前 |
2608 起 |
|
|---|---|---|
| 列數 | 548,032 | 915,171(繁體版,×1.67) |
| 唯一檔名 | 548,032(=列數,全域去重) | 777,464 |
| 涵蓋條目 | 245,357 | 297,083 |
| 圖說殘留標記 | 10.87% | 0.00% |
| 條目名不是繁體(繁體檔) | 43.8% | 0.0% |
| 欄位 | url/file_name/page/title/tag |
再加 page_id/page_url/alt/section,圖說欄改名 caption |
列數增加來自兩處性質不同的改變。抓得比較全:<gallery> 區塊(舊版完全不看,
約 18%)、[[檔案:/[[Image: 等其他前綴(5.6%)、跨行寫的圖片語法(0.3%)。
保留使用語境:舊版按檔名全域去重(列數恰等於唯一檔名數),同一張圖在不同
條目、配著不同圖說只留一列——那正是圖文配對最有價值的部分。
品質差異比數量更關鍵:舊版有 10.87% 的圖說夾著沒清乾淨的 wiki 標記
([[歐幾里得]],西元前三世紀的[[古希臘]][[數學家]]),繁體檔裡有 43.8% 的條目名
根本沒轉成繁體(数学)。舊版沒有 page_id,無法與正文資料集對照;沒有
section,不知道圖屬於哪一段。
2607 以前的版本仍存放在各 repo 的 YYMM/ 月份資料夾裡,需要的話可以直接讀舊路徑。
被取代的實作留在 legacy/,含它們為什麼被換掉的說明。
只移除確定是標記的東西,絕不刪除自然語言內容。
這條原則決定了整個 pipeline 的做法:
- 🧩 模板展開而非刪除 —
{{lang}}、{{flag}}、{{bd}}、{{coord}}、{{convert}}渲染後是可見文字,整批刪掉會讓條目失去實質內容(人物生卒年、外文原名、座標) - 📊 表格轉文字而非丟棄 — 表格佔可見內容約 15%,賽事成績、參賽名單、人口統計都在裡面
- 🖼️ 圖庫的圖說是內容 —
<gallery>的圖說在渲染後就是可見文字,整塊刪掉會讓「一世一元制」的君主列表(整份寫在 gallery 裡)消失。曾經用「太短就丟掉」篩選,量過全量 41,500 頁、254,455 條圖說後發現那會砍掉 54%,其中不乏真內容,所以改成一律轉成清單項,不做長度篩選 - 💻 程式碼與公式逐字保留 — 圍欄內不套用散文的排版規則。一般行之間要補空行,同一條規則套到程式碼上會把每一行撐成一個段落;縮排被空白正規化吃掉,Python 範例就失去語意
- 🔤 引號裡的符號是內容 —
「*」:打榜中、「其符號為「$」」不能因為「不是文字」就連引號一起刪掉,圖例與符號說明整篇都靠它 - ⚖️ 判斷不能取決於語言版本 — 繁簡兩版出自同一份中間層,結構必須一致。凡是拿字數當門檻的規則都會因譯名長度而換邊(沙特/沙烏地、肯尼亞/肯亞),收錄門檻統一用簡體版判定,清理規則也不用字數預算
- 🧷 所有掃描都設上限 — 括號配不到對時無條件吃到結尾,一篇條目就整個消失(實測《鳥》少 7,800 字、《聖雄甘地》少 33,000 字)。跨度異常就退回只丟標記本身
- 🏷️ 優先採用編者標註 —
-{zh-tw:賽局理論;zh-cn:博弈论}-是人工寫的地區用詞對照,品質高於任何自動轉換 - 🔍 規則從 dump 裡挖,不寫死名單 — 手寫名單永遠補不完,也說不出「補到哪裡算完整」。資訊框的欄位名、維護模板的身分都是維基自己的渲染規則,掃一次 Template 命名空間就有(見下節)
- 🧪 逐字區塊不做繁簡轉換 — 與 MediaWiki 的 LanguageConverter 一致,
code/pre/syntaxhighlight/math是不轉換區。轉了會改掉程式語意:Console.WriteLine("简体字符串")的字串內容被改寫、// 构造函数的「函数」被台灣詞表換成「函式」 - 📄 文檔級而非段落級 — 一個條目一筆記錄,保留章節結構;切碎成段落會破壞長距離依賴,也不適合現在的長 context 訓練
- ✍️ 不做排版美化 — 語料應貼近真實文本分佈。排版空格是不可逆的有損轉換,需要的人事後自己加只要三行
- 🔗 可回溯原文 — 每筆帶
id與url,維基百科是 CC BY-SA,署名需要能連回原文
維基的渲染規則寫在它自己的模板頁裡。與其猜,不如掃一次 Template 命名空間把答案取出來。
側邊資訊框在版面上不屬於行文,但生卒年、面積、成立年份這些事實往往只寫在裡面。
因此資訊框整塊丟棄前會先把欄位抽出來,接在文末的 ## 基本資料 章節。
欄位名不是猜的——MediaWiki 渲染時標籤就寫在模板頁的 | label9 = 出生 旁邊:
Template:Infobox person/core 條目輸出
| label9 = 出生 ─────► * 出生:1879年3月14日
| data9 = {{{birth_date|}}}
infobox_labels.py 掃出三種對應關係:
| 種類 | 來源 | 例子 |
|---|---|---|
| 靜態標籤 | label 欄的文字 |
occupation → 職業 |
| 動態標籤 | label 欄本身是另一個參數 |
established_date 的欄位名寫在條目自己的 established_title 裡,所以輸出是 設立地級市:1979年 |
| 參數別名 | {{{name|{{{名字|{{{姓名|}}}}}}}}} 巢狀鏈 |
同一欄位的不同寫法共用標籤 |
再加上:條目自己填的欄位名最優先、鍵本身是中文就直接用、編號變體(term_start2)
沿用基礎鍵的標籤。
還有一批模板的版面是 Lua 模組算出來的(中國行政區那一族),模板頁裡根本沒有
label = …,這裡挖不到。那就換個地方問維基:把用到該模板的條目渲染出來,
讀 <th class="infobox-label"> 與 <td class="infobox-data">,再拿每一列的值回頭
比對條目原始碼的參數值——值精確相等就認定那一列屬於那個參數
(qa/label_from_render.py)。只收三篇以上條目一致的,因為錯的標籤比英文原鍵
糟得多:它斷言了一件假事實。
實測英文原鍵從 75.4% 降到 16.4%,值等於標題的冗餘欄位從 3.0% 降到 0.07%。 查不到標籤的欄位保留原鍵,不因為「標籤不好看」而丟掉事實。
導覽框(Navbox、Sidebar)與警告框跟資訊框共用命名慣例,但內容是導覽元素,不抽事實。
{{Or}}、{{Delete}}、{{Refimprove}} 這類渲染成頁面上的訊息方塊,是給編者看的。
它們有結構訊號:本體一定引用訊息方塊家族(ambox 條目、imbox 圖片、cmbox 分類、
tmbox 討論、ombox、fmbox)。用這個從 dump 挖,取代原本手寫的 93 個名字。
但整族丟棄的模板裡若有程式碼或公式,要撈出來——{{efn|縮排示例:<syntaxhighlight>…}}
是註腳外殼,裡面那段 Python 範例沒有別的地方可去。
{{MLT}}、{{USA}} 展開後才是國名。查不到就只能丟掉,於是《9月21日》的
「馬爾他:獨立日」變成「-:獨立日」。有參數的模板不收,但參數區塊前的固定文字
會留下——那是確定會渲染的部分。切點若落在 HTML 標籤中間要退到標籤之前,
否則會留下 <div class="notelist" style="list-style-type: 這種沒有 > 的半截標籤
(全量 495 個模板是這個形態)。
{{#if:}}、{{#ifexpr:}}、{{#switch:}} 的條件我們算不出來,但留著原文一定是錯的。
取「成立」那一支——那是編者預期的主要輸出。{{#expr:}} 則直接算出來,否則
《Pokémon GO》會變成「一共有種寶可夢被正式開放」。
<syntaxhighlight>/<pre>/<code>/<math>/<nowiki> 裡的內容要一個字元都不動。
這比聽起來難,因為整條清理鏈的每條規則都想動它們:
| 想動它的規則 | 不擋住的話 |
|---|---|
| 段落切分(行間補空行) | 每一行程式碼變成一個段落 |
| 空白正規化 | Python 的縮排消失,語意沒了 |
| 空括號清理 | f() 變成 f;顏文字的 ( ) 整行被刪 |
行首 # 當有序清單 |
#include 變成 - include |
| 表格切分 | | 把公式與程式碼攔腰切斷 |
| HTML 標籤移除 | < 到下一個 > 之間整段消失 |
| 孤兒括號清理 | LaTeX 的巢狀括號被成串刪掉 |
| 繁簡轉換 | Console.WriteLine("简体字符串") 的字串內容被改寫 |
| 註解移除 | 示範註解寫法的 <pre><!-- … --></pre> 整塊消失 |
做法是進清理鏈之前先把這些區塊裡的敏感字元換成私有區佔位符
({}|<#()[]'*=:;> 與空白、tab、全形空格),走完整條鏈才還原。
全形空格特別容易漏:顏文字與 ASCII 藝術完全靠它排版。2channel文字人物 的
( ・∀・) 若沒遮起來,空白正規化會收掉全形空格、「空括號」規則會把
( ) 整行刪掉——圖案毀了,但所有殘留檢查都是綠的,逐字比對也只
算它「只差空白」。補上遮罩後,程式碼區塊的完全相符率從 93.3% 升到 94.2%。
繁簡轉換跳過這些區塊,與 MediaWiki 的 LanguageConverter 一致——它同樣把
code/pre/syntaxhighlight/math 標成不轉換區。
但「不轉換區」認錯了範圍更糟。行內公式寫成 $…$,而 $ 在中文條目裡更常
是貨幣符號或 $MFT、$Volume 這種 NTFS 檔名——兩個這樣的 $ 之間夾著的整段
散文會被當成公式,於是完全不做繁簡轉換。NTFS 整篇大半留在簡體、
反恐精英:全球攻勢 的「$250,000」到「$1,000,000」之間留著「举办了赛事系列」。
判準是量出來的:全庫 250,956 個 $…$ 區段裡 7,359 個含中文,其中只有 126 個
(1.7%)帶 LaTeX 記號——那些是 \text{光照} 這類化學反應式,是真公式;其餘
7,233 個全是貨幣與檔名。所以含中文又沒有 LaTeX 記號的就不是公式;否決時
只跳過開頭那一個 $,免得貨幣符號把後面真正的公式一起吃掉。
修正後,繁體資料集散文裡的「簡體專用字」(繁體中根本不存在的字)從 2,360 篇 降到 3 篇;程式碼區塊裡的中文註解依設計仍不轉換,報表分開列。
另一面:整族丟棄的外殼裡若有程式碼或公式,要撈出來。編者會把方法定義放進
<ref>(Smalltalk 的五段集合類別實作)、把範例放進 {{efn}} 註腳
(Python 的縮排示例)、把推導放進 <ref group="note">(參宿四 的十條半徑
公式)。外殼不進正文,但裡面的內容沒有別的地方可去。
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python -r requirements.txt目前多進程解析明確使用 fork 來共享大型對照表,因此支援 Linux 與 WSL;
Windows 原生 Python 沒有 fork,請改在 WSL 執行。
每月例行作業(下載 → 解析 → 生成四組資料集與圖片資訊 → 檢查 → 上傳):
.venv/bin/python wiki_cli.py --generate-all只想生成不上傳:加
--no-upload
下載器會從 Wikimedia 官方 SHA-1 清單取得真正的 dump 快照日期,並在下載完成後
同時驗證檔案大小與 SHA-1。第一次解析某月份時也會自動掃描並建立 Wikidata
動態欄位快取;純離線處理可加 --skip-wikidata,但相關欄位值可能留空。
單步操作:
.venv/bin/python wiki_cli.py --download # 下載 dump
.venv/bin/python wiki_cli.py --to-md # 解析成中間層(已完成會跳過,--force-md 強制重跑)
.venv/bin/python wiki_cli.py --to-pretrain --lang tw # 由中間層生成純文字版與 omni 版
.venv/bin/python wiki_cli.py --extract-images --lang tw # 提取圖片資訊
.venv/bin/python wiki_cli.py --upload # 上傳(--dry-run 可試跑)dump.xml.bz2
│
│ metadata_store.py 掃一次 Template 命名空間,同時建立:
│ template_store 無參數模板、國名、維護模板名單
│ infobox_labels 「參數 → 中文標籤」對照表
│ wikidata_store.py 查 {{wikidata|…}} 取值模板的實際數值(API,結果快取)
▼
parsed/YYYYMM/*.json 離線對照表(可重複使用,重建時保留)
│
│ wiki_parser.py 模板展開、表格轉文字、語言變體保留、圖片位置標記
▼
parsed/YYYYMM/pages-*.jsonl 語言中立的中間層(分片 JSONL,tw/cn 共用)
│
│ wiki_text.py 殘留標記清理、繁簡轉換、空白與標點正規化
│ md_to_dataset.py 章節組裝、殘句修剪、Parquet 輸出
▼
output/{tw,cn}/train-*.parquet 純文字版
output/{tw,cn}/omni/train-*.parquet 圖文交錯版
│
│ image_extractor.py 另外掃一次 dump,抽圖片與圖說(tw/cn 單掃雙出)
▼
output/{tw,cn}/wiki_images_dataset*.jsonl
四組 Parquet 是同一次走訪中間層產出的:先建語言中立的正文,再派生繁/簡兩版, 每版再派生純文字與 omni。逐版各跑一次的話,同一頁要重複組裝四次。
中間層存成分片 JSONL 而不是一條目一檔:一次 dump 有 150 萬篇,百萬個小檔案在 NTFS 上的建檔、列目錄、刪除成本極高,而中間層的用途只是讓兩個語言共用同一次解析。
parsed/YYYYMM/*.json 是離線對照表,跟中間層放在同一個目錄但生命週期不同:
中間層每次重建都重生,對照表要留著。{{wikidata|properties|P374}} 這種取值模板的值
不在 pages-articles dump 裡,不補的話法國市鎮條目會寫成「INSEE市鎮編碼為。」;
完整的 Wikidata dump 實測 95.5 GB,但全量掃過只有 41,096 篇條目用得到,所以改用
API 按條目批次查再快取,代價小三個數量級。
轉換優先序:
- 編者標註
-{zh-tw:…;zh-cn:…}-— 人工寫的,最準 - 維基官方轉換表
data/zhconv.json(取自 MediaWikiZhConversion.php) - 台灣慣用詞白名單
tw_vocab.py— 補維基表偏保守漏掉的 IT 詞
不用 OpenCC 的 s2twp:它為軟體在地化設計,在百科語境誤轉率極高。12 句一般散文
測試錯 7 句(大力支持→大力支援、運動會項目→運動會專案、重整程序→重整程式、
文件由政府發布→檔案由政府釋出),而維基表 0 誤轉。
基礎表與地區用詞表合併成一張表跑單一趟最長匹配,跟 MediaWiki 一樣。
不能先做一次繁簡正規化再套地區用詞表:兩張表的鍵不是同一種字體
(zh2TW 多是簡體 软件→軟體,zh2CN 卻多是繁體 軟體→软件),先正規化會讓
31% 的 zh2TW 與 57% 的 zh2CN 條目永遠查不到。以網站渲染結果為標準答案實測
(220 篇、繁體 145,828 字/簡體 283,190 字)的逐字差異率:
| 串接方式 | 繁體 | 簡體 |
|---|---|---|
先 zh2Hans 正規化再套變體表 |
1.71% | 0.15% |
| 合併成一張表跑單一趟 | 1.44% | 0.09% |
tw_vocab.py 的白名單會讓繁體與網站的差異略為上升(0.79% → 0.85%),這是刻意的:
維基那張表不轉 網絡、用戶、計算機,但這個資料集的目標是台灣慣用中文,
所以在這些詞上主動偏離網站。白名單只收在一般語境不會有其他意思的詞。
白名單需要詞邊界護欄。純子字串替換會把更長的詞從中間切斷:
| 原文 | 沒有護欄 | 命中位置 |
|---|---|---|
| 電視頻道 | 電影片道 | 電視|頻道 中間的 視頻 |
| 攻打印度 | 攻列印度 | 攻打|印度 中間的 打印(打印 的出現全是這種) |
| 滑鼠標準 | 滑滑鼠準 | 滑鼠|標準 中間的 鼠標 |
| 用戶名稱 | 使用者名稱稱 | 用戶|名稱 中間的 用戶名 |
詞表不自己編也不外掛第三方詞庫,取自維基百科自己的條目標題
(title_words.py,81 萬個 2–6 字純中文標題):電視、印度、滑鼠、標準、
名稱 全是條目。命中若被某個詞跨過邊界壓住就取消替換,並退回去試同一位置
較短的白名單詞(用戶名 被擋下時仍會套用 用戶)。護欄只會取消替換不會
製造替換,詞表不完整時最差就是退回原本行為。
白名單本身也依同一標準清過一輪:判準不是語感,是把條目丟給 API 以
variant=zh-tw 渲染,看站上的台灣正體版實際顯示什麼。站上顯示「小學」「循環」
「函數」「代碼」「地鐵」「摩托車」「自行車」——這些條目我們轉了就是錯
(國民小學→國民國小、《循環日報》→《迴圈日報》、行政區劃代碼→行政區劃程式碼、
上海地鐵→上海捷運)。另有一批底層轉換表已經處理(導彈→飛彈、激光→雷射),
留在白名單只多一次切詞風險;還有在台灣另有意思的(土豆是花生、中央空調
不會說成中央冷氣、菠蘿麵包)。67 條清成 43 條,每一條的移除理由與證據都寫在
tw_vocab.py 裡。
清理規則寫錯不會報錯,只會靜靜地弄丟內容。所以驗證分層,每層抓不同性質的問題—— 曾經有一次「所有殘留字元檢查都是綠的」,但程式碼被壓成一行,是人工翻輸出才發現的。
| 檢查 | 抓什麼 | 目前結果 |
|---|---|---|
| 行為黃金案例 | 每個修好的缺陷留一個案例,改動後必跑 | 88 條全過 |
| 回溯壓力測試 | 正則指數級回溯(曾讓 13 個 worker 各燒 3 小時 50 分) | 28 項全過 |
| 修正驗證 | 每個修正回出貨資料查那一篇條目 | 13 條全過 |
| 全量硬性缺陷 | 16 類必須為 0 | 空正文、模板/表格/連結/ref/HTML/實體殘留,以及私有區字元、控制字元、連續空行等 全部 0 |
| 結構不變量 | 圍欄成對、標題層級連續、無空章節 | 圍欄 0、空章節 0、首行 0 |
| 繁簡結構對等 | 兩版只該差在字體 | 抽驗 20,000 篇:0 個阻斷差異;1 個來源明定的地區譯文表格警示 |
| 字體純度¹ | 散文裡有沒有「目標字體根本不存在的字」 | 繁體 3 篇/10 個字、簡體 10 篇/41 個字(逐字區內的中文註解依設計不轉換,另計) |
| 詞邊界¹ | 台灣詞彙白名單有沒有切斷更長的詞 | 已知的 14 種切錯痕跡在 148 萬篇裡 0 次(13 次命中經查全是原文本來就這樣寫) |
| 程式碼逐字比對¹ | 回頭掃 dump,每個區塊拿去輸出裡找 | 繁體 94.122%、簡體 93.961% 完全相符;只差空白約 0.9% |
| 公式逐字比對¹ | 同上 | 繁體 98.661%、簡體 98.669% 完全相符;被改動約 0.42%、整條消失約 0.92% |
| 表格內容比對¹ | 同上 | 繁簡均 93.33% OK(各抽驗 600 篇;探針儲存格保留率 97.3%/97.1%) |
| omni 一致性 | <image> 個數 == images 長度 |
0 誤差(繁體 1,438,406、簡體 1,438,407 張圖) |
| 圖片資料集 | 欄位、網址、殘留標記、空文字、重複與 JSON | 8 類缺陷全 0 |
| 線上逐句對照¹ | 對照維基渲染結果(外部標準答案) | 覆蓋 88.5%、吻合 94.2%、每篇中位數 100% |
| 資訊框標籤¹ | 欄位名是中文還是英文原鍵 | 英文原鍵 75.4% → 16.4%,值等於標題 3.0% → 0.07% |
¹ 標 ¹ 的項目來自 2609 輪的全量稽核(各自的稽核工具,非每月閘門),2610 未重跑;其餘列為 2610 實測(另新增 3 個表格儲存格程式碼的單元測試,共 56 個)。
像標記的真實內容另行判讀:A<B> a; 是 C++ 模板語法、契維語的 <p> 是
音韻學正字法、{{1,2,3}} 是集合論記號;<nowiki>[[X]]</nowiki> 在維基渲染後
本來就是字面方括號。硬性閘門會先辨識逐字區與來源語意,不把這些內容誤報成殘留。
整塊消失逐筆歸因:程式碼 62 筆(註解內 19.4%、跳過章節 3.2%、圖說內 3.2%)、 公式 1,322 筆(圖說內 34.1%、註解內 12.8%、跳過章節 1.8%、丟棄模板 0.9%)。 圖說依設計只進圖片與 omni 資料集,是三份資料的分工不是遺失。
刪除型缺陷靠殘留檢查抓不到,所以正面驗證。 殘留檢查只能看見「多出來的東西」, 看不見「不見的東西」——公式被整條刪掉時所有殘留檢查都是綠的。因此程式碼、公式、 表格三項直接回頭掃 dump 做逐字比對。
稽核自己也會錯。 [[nodiscard]] 是 C++ 屬性、{{1,2,3}} 是集合論記號、
1-{{2-甲基…}偶氮} 是化學命名法——這些不是殘留標記。檢查前要先挖掉圍欄與公式
區段,否則會把真實內容當成缺陷「修掉」。
所以每一筆都要歸因,不能停在抽樣。 殘留標記逐筆分類的結果:殘留模板 114 筆、
殘留連結 191 筆全部是 <nowiki> 字面值或真實內容,沒有一筆是該修的。這個
結論靠抽樣說不出口——實際上第一版分類器就判錯了 50 筆,因為預篩漏了 </>
的遮罩碼位。
繁簡對等抓到過單一語言看不出來的 bug。 一條「撈註腳程式碼」的規則把自閉合的
<ref name="x" /> 當成開頭標籤,跟遠處的 </ref> 配成一對,中間整段正文被吃掉
——捷爾諾波爾州 的簡體版三個章節全部消失,而繁體版完全正常,所有殘留檢查也
都是綠的。兩版一比才露餡。
「已修」不等於「資料裡是對的」。 每個修好的缺陷都對應一篇實際壞過的條目,
qa/verify_fixes.py 直接去出貨的 Parquet 裡查那一篇——該在的在不在、該消失的
消失沒。這道關卡不是形式:最近一輪修掉的 11 個缺陷裡,有 5 個是在修別的東西時
自己引入的,其中兩個(自閉合 <ref> 吃掉整段正文、跨越逐字區邊界的註解只
被刪一半)單元測試全過、看起來完美,是全量稽核才抓到的。
稽核工具自己也要被稽核。 同一輪裡,我寫的檢查工具錯了五次:分類器的預篩漏了
</> 的遮罩碼位(把 50 筆忠於原文誤判成真殘留)、證明工具拿挖空後的文字取
指紋(x 佔位當然找不到)、標籤反推用「包含」配對(配出 sock1 → CPU插座)、
消失歸因的探針只取開頭 60 字(把還在的程式碼算成消失,數字虛報一倍)、反推收了
通用欄位(stat1value 的標籤其實寫在條目自己的 stat1label 裡)。
照著稽核結果回原始碼查證,是唯一能發現這些的方法。
線上對照:qa/webcmp.py 拿線上維基百科的渲染結果逐句比對,覆蓋率 88.5%、
吻合率 94.2%、每篇覆蓋率中位數 100%。這是唯一的外部標準答案——它曾逼出兩個
自我驗證永遠看不到的缺陷:{{bd|1989年|4月18日}} 只有生日卻加了破折號、以及
一條「獨佔一行的模板要夠長才算正文」的長度門檻把 25 字的引文整段刪掉
(那正是專案第一原則禁止的事,而它一直躺在自己的程式碼裡)。
做得到與做不到:16 類可程式判定的硬性缺陷已全量歸零;原文本身的畸形標記與 長得像標記的真實內容則保留來源語意並列為品質警示。能做到的是每一類問題都有 結構性的處理與證據,而不是靠列名單或把可疑內容整段刪掉。
qa/ 的核心黃金案例、壓力測試與出貨閘門會進版控;大型驗證資料、日誌及
一次性分析探針由 .gitignore 排除:
| 工具 | 做什麼 |
|---|---|
cases.py |
行為黃金案例,每個修好的缺陷留一個,註解寫明哪一篇條目壞過 |
stress.py |
正則回溯壓力測試(曾讓 13 個 worker 各燒 3 小時 50 分) |
verify_fixes.py |
逐項驗證修正有沒有真的進到資料裡 |
validate_full.py |
全量硬性缺陷與完整性 |
invariants.py |
結構不變量:圍欄成對、標題層級連續、無空章節 |
parity.py |
繁簡結構對等——單一語言看不出來的 bug 靠它現形 |
omni_audit.py |
<image> 個數 == images 長度 |
image_audit.py |
圖片資料集的欄位、網址、殘留標記 |
公開工具的完整指令、通過條件與清理政策見 qa/README.md。回掃 dump、
線上逐句對照與歸因探針屬發布期間的延伸審查;探針結論會轉成黃金案例,舊日誌與
過濾後 dump 不進版控,避免把不可重現的舊輪次結果當成現行閘門。
全量一輪約 55 分鐘(20 核心)。主要是「少掃幾次 dump」換來的——實測 producer 階段有 75% 花在 bz2 解壓、25% 在 XML 切頁,所以減少掃描次數比微調正則有效得多。
| 優化 | 實測 |
|---|---|
| 模板表與資訊框標籤表合併成單次掃描 | CPU 時間 −55%(三份 JSON 逐 byte 相同) |
| 四組資料集共用一次中間層走訪 | 5,000 筆實測 −64%(四版本 SHA-256 相同) |
| 圖片 tw/cn 單掃雙出 | 1,645 秒 → 737 秒,2.2×(同一趟掃描產生兩種字體) |
| 稽核共用過濾檔 | 494 萬頁 → 88.5 萬頁(17.9%) |
| 稽核平行跑 | 40 分鐘 → 12 分鐘 |
bz2file → 標準庫 bz2 |
每次掃描 −7~12% |
| Parquet zstd 9 → 3 | 寫入 CPU 快 4.2 倍,體積僅大 13% |
記憶體:19 個 worker 的 RSS 加總 3.54 GiB,但 PSS 只有 1.01 GiB——fork 的 copy-on-write 有效,四張對照表(模板 47k、國名 6.3k、Wikidata 41k、資訊框標籤) 是共用的,不是每個 worker 各一份。
--generate-all 生成完會自動推送,也可單獨執行。五個 repo(繁簡各兩份、圖片一份)
一起處理:
.venv/bin/python wiki_cli.py --upload
.venv/bin/python wiki_cli.py --upload --dry-run # 試跑,不實際寫入擇一即可(優先順序由上到下):
.venv/bin/python wiki_cli.py --upload --hf-token hf_xxx
export HF_TOKEN=hf_xxx
hf auth loginrepo 的 root 永遠是最新版本,load_dataset() 預設讀到的就是最新資料;舊版本
自動歸檔到 YYMM/ 月份資料夾:
wikipedia-zh-tw/
├── train-00000-of-00007.parquet # 最新版本
├── dataset_version.json # 版本記錄(由工具維護)
├── 2607/ # 上個月
└── 2606/
歸檔用 HF 的 server-side copy,不需重新上傳幾 GB 的舊檔案。歸檔月份自動判斷
(dataset_version.json → 現有月份資料夾 → 最後 commit 日期)。README 的 dump
日期、版本區塊、欄位說明會一併更新。
omni 版是各自獨立的 repo,不跟純文字版混在一起——它多一個 list-of-struct 的
images 欄位,schema 不同,放同一個 repo 會讓 load_dataset() 的自動推斷失敗。
推上公開 repo 前會跑一輪檢查,任何一項不過就中止:
| 項目 | 說明 |
|---|---|
| 分片完整性 | 編號連續、與檔名宣告的總數一致 |
| Schema | 欄位與型別正確 |
| 文檔級確認 | 抽樣平均長度 < 300 字就報錯(防止退回段落級) |
| 排版空格 | 偵測 pangu 殘留 |
| 繁簡一致性 | 抽樣比對,抓 --lang 傳錯 |
| 資料量落差 | 與上一版比較 |
生成一次要跑一小時,所以
--generate-all會在開始前先驗證 HF 認證與 repo 權限,不會等到最後一步才失敗。
| 目錄 | 保留單位 | 說明 |
|---|---|---|
downloads/ |
YYYYMMDD |
最新 2 個月份的 dump |
parsed/ |
YYYYMM |
最新 2 個月份的中間層 |
output/{lang}/ |
無月份 | 純文字版 Parquet 與圖片 JSONL |
output/{lang}/omni/ |
無月份 | 圖文交錯版 Parquet |
舊版本在 Hugging Face 上有 YYMM/ 存檔,本地不需要一直留著。保留月數改
wiki_cli.py 的 KEEP_MONTHS。
| 檔案 | 說明 |
|---|---|
wiki_cli.py |
統一 CLI 入口 |
wiki_downloader.py |
下載維基百科 dump |
metadata_store.py |
掃一次 Template 命名空間,同時建立下面兩張表 |
template_store.py |
無參數模板、國名對照、維護模板名單 |
infobox_labels.py |
從模板頁的 label/data 欄抽資訊框參數的中文標籤 |
wikidata_store.py |
查 {{wikidata|…}} 取值模板的實際數值並快取 |
wiki_parser.py |
wikitext 解析:模板展開、表格轉換、語言變體保留 |
md_converter.py |
驅動解析並寫出中間層 |
page_store.py |
中間層儲存(分片 JSONL) |
wiki_text.py |
文本正規化 |
zhconv.py + data/zhconv.json |
繁簡轉換(維基官方表) |
tw_vocab.py |
台灣慣用詞白名單(含詞邊界護欄,不切斷更長的詞) |
title_words.py |
由維基條目標題建中文詞表,供詞邊界判斷 |
md_to_dataset.py |
章節組裝、四組資料集輸出 |
image_extractor.py |
從 dump 提取圖片資訊(圖說沿用 wiki_parser/wiki_text) |
image_downloader.py |
依 JSONL 下載圖片 |
hf_uploader.py |
上傳前檢查、月份歸檔、推送 HF |
legacy/ |
改版前的實作,保留備查,不參與現行流程 |
- 本專案不包含大型已生成資料(
downloads/、parsed/、output/、images/) - 要重現請依上述指令執行,需要足夠磁碟空間與網路帶寬
- 文字經繁簡轉換,若需完全未轉換的原始文本請自行從 dump 生成
- 條目內容僅保留知識性正文,已去除參考資料、外部連結、導航框、維護模板等非內容區塊。
章節是否屬於「參考資料」不只看標題:
來源、資源、版本歷史、許可這些詞在 很多條目裡是正文(OpenBSD > 許可講的是 ISC 授權條款,咖啡因 > 來源是咖啡因 的天然來源與含量表),因此標題命中清單時還要看內文有沒有成段散文或成形的表格 - 列表型與年表型條目保留其原始形態(列表、表格列),不強行改寫成散文
- 沒有長度過濾。「PySide是跨平台的圖形使用界面框架Qt的Python版本。」這種一句話 條目是完整的知識內容,維基官方資料集也不做長度過濾——要篩隨時可以自己做, 被丟掉的救不回來
請參閱 LICENSE。資料本身來自維基百科,採用 CC BY-SA 授權,使用時請保留
url 欄位以符合署名要求。
歡迎提 Issue 與 Pull Request,請參閱 CONTRIBUTING.md。
發現安全問題請參閱 SECURITY.md。
- 一般問題與建議:GitHub Issues
- Email:huhu11256@gmail.com
- 維護者:YuhuanStudio
- 安全漏洞:請依 SECURITY.md 使用私密通報,勿建立公開 Issue