Skip to content

About

維基百科中文語料處理工具,用於生成預訓練與多模態資料集,支援繁簡體轉換、高質量過濾、統一 CLI 入口 🚀

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WikiZH_Dataset

License Python GitHub Stars GitHub Forks GitHub Issues Hugging Face

中文維基百科語料處理工具,每月從官方 dump 生成可直接訓練的資料集。

一次解析,三種形態:純文字給語言模型、圖文交錯給多模態、圖文配對給圖像理解。 三者出自同一次解析、同一套清理規則,可以用 id 互相對照。

資料集 形態 規模
HF 純文字 · 台灣正體 🇹🇼 149 萬篇
HF 純文字 · 簡體 🇨🇳 149 萬篇
HF 圖文交錯 · 台灣正體 🖼️📝 149 萬篇 · 142 萬張圖
HF 圖文交錯 · 簡體 🖼️📝 149 萬篇 · 142 萬張圖
HF 圖文配對 🖼️ 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 並以 $ 包住

圖文交錯(omni)

<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-articles dump 裡,本資料集 不含授權欄位,使用前請自行向 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 以前與 2608 起的差異

整條處理流程重寫過。 這不是單純的月度刷新——資料的形狀變了。 若你正在用 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,署名需要能連回原文

🔍 從 dump 挖規則,而不是手寫名單

維基的渲染規則寫在它自己的模板頁裡。與其猜,不如掃一次 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 按條目批次查再快取,代價小三個數量級。

🔁 繁簡轉換

轉換優先序:

  1. 編者標註 -{zh-tw:…;zh-cn:…}- — 人工寫的,最準
  2. 維基官方轉換表 data/zhconv.json(取自 MediaWiki ZhConversion.php)
  3. 台灣慣用詞白名單 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 各一份。


🤗 上傳到 Hugging Face

--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 login

📁 版本管理

repo 的 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。

🔗 相關連結

📬 聯絡

About

維基百科中文語料處理工具,用於生成預訓練與多模態資料集,支援繁簡體轉換、高質量過濾、統一 CLI 入口 🚀

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages