Spaces:
Running on Zero
A newer version of the Gradio SDK is available: 6.26.0
title: 繁中維基語意搜尋 zh-TW Wiki Semantic Search
emoji: 🔍
colorFrom: indigo
colorTo: pink
sdk: gradio
sdk_version: 6.20.0
app_file: app.py
pinned: false
license: mit
short_description: bge-m3 + FAISS 語意搜尋 vs BM25(50k 繁中維基段落)
models:
- BAAI/bge-m3
datasets:
- steven0226/zhtw-wiki-semsearch-index
🔍 繁中維基語意搜尋引擎(bge-m3 × FAISS × Gradio)
對繁體中文維基百科隨機抽樣的 50,000 個段落建立語意索引,並排比較 語意搜尋(bge-m3 + FAISS) 與 關鍵字搜尋(BM25 + jieba), 讓「同義改寫也搜得到」的差異一眼可見。
🚀 線上 Demo:Hugging Face Space (免費 ZeroGPU Space,app 為純 CPU 程式碼;冷啟動需幾分鐘)
為什麼做這個?
關鍵字搜尋只認得字面上的詞:搜「天空為什麼藍藍的」,BM25 只能比對 「天空」「藍」這些字,找不到用「瑞利散射」「大氣層」描述同一件事的段落。 語意搜尋把整句話變成 1024 維向量,「意思相近」的段落在向量空間裡自然靠近—— 即使一個字都沒重疊。Demo 裡的橘色標記就是「語意找得到、關鍵字找不到」的結果。
架構
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────────┐
│ 本機 RTX 4090 │ │ HF Dataset repo │ │ HF Space(ZeroGPU) │
│ │ │ │ │ │
│ prepare_data.py │ │ index.faiss (205MB) │ │ app.py(Gradio) │
│ build_index.py ├─────▶│ metadata.parquet ├─────▶│ ・bge-m3 編碼查詢 │
│ (fp16 批次編碼) │ 上傳 │ (title/text/url) │ 啟動時 │ ・FAISS top-10 │
│ │ │ │ 下載 │ ・BM25 對照組 │
└─────────────────┘ └──────────────────────┘ └─────────────────────┘
重的 embedding 計算(50k 段 × bge-m3)在本機 GPU 做,Space 只負責: 把查詢句編碼成向量(CPU、約 1–3 秒)→ FAISS 內積搜尋(<1ms)→ 顯示結果。
bge-m3 是什麼?
BAAI/bge-m3 是北京智源(BAAI)的多語言 embedding 模型,基於 XLM-RoBERTa-large(~568M 參數):
- 多語言:100+ 語言共用同一個向量空間,中文查詢可以配對英文段落
- Multi-Functionality:同時支援 dense、sparse(lexical)、multi-vector (ColBERT)三種檢索——本專案用 dense 向量(1024 維)
- 免 instruction prefix:不像 bge-*-zh-v1.5 需要「為這個句子生成表示…」前綴
- 向量 L2-normalize 後用內積(= cosine 相似度)配
faiss.IndexFlatIP做精確搜尋 (50k 規模不需要 IVF/HNSW 近似索引)
語意 vs 關鍵字:實際例子
| 口語查詢 | 語意搜尋找到 | BM25 的困境 |
|---|---|---|
| 天空為什麼藍藍的 | 瑞利散射、大氣光學條目 | 「藍藍的」比對不到「散射」 |
| 為什麼感冒好了比較不容易再中一次 | 免疫記憶、抗體條目 | 全句沒有「免疫」兩字 |
| 月亮為什麼有時候圓有時候只剩彎彎的一條 | 月相條目 | 「彎彎的一條」不在任何條目裡 |
(實際結果依 50k 隨機抽樣內容而定,歡迎在 Demo 裡自己試)
重現步驟
# 0. 環境(Windows + NVIDIA GPU;Linux 把路徑分隔換掉即可)
uv venv .venv --python 3.12 --seed
.venv\Scripts\Activate.ps1
uv pip install -r requirements-local.txt --index-strategy unsafe-best-match
# 1. 資料:下載繁中維基(8.2GB)→ 清理 → 抽 50,000 段(seed=42,可重現)
python scripts/prepare_data.py
# 2. 索引:GPU fp16 批次編碼 + FAISS IndexFlatIP
python scripts/build_index.py
# 3. 上傳到 HF dataset repo(HF_TOKEN 放專案根目錄 .env)
python scripts/upload_index.py
# 4. 本機端到端測試(先本機檔案、再走真實下載路徑)
$env:DATA_DIR="data"; python app.py
Remove-Item Env:DATA_DIR; python app.py
# 5. 部署 Space
python scripts/deploy_space.py
設計筆記
- HF 免費帳號政策(2026):新的 Gradio Space 只能建在 ZeroGPU 硬體 (cpu-basic 需 PRO 訂閱)。本 app 全程純 CPU 程式碼、不呼叫 GPU, 不消耗訪客的 ZeroGPU 配額;torch 必須 pin ZeroGPU 支援清單內的版本(2.8–2.11)
- 模型只載一次:app.py 在 module top-level eager 載入(Space 的 Building/Starting 畫面天然就是 loading 提示),並先做一次暖身編碼
- BM25 基線:rank_bm25 + jieba 斷詞,啟動時對同一批 50k 段落現場建索引, 與語意側用完全相同的語料對照才公平
- CJK 路徑陷阱:faiss 的
write_index/read_index用窄字元 fopen, 在含中文的 Windows 路徑會失敗——全程改用serialize_index/deserialize_index+ Python byte I/O
效能數據
| 項目 | 數值 |
|---|---|
| 50k 段落 GPU 編碼(4090, fp16, batch 128) | 52.2 秒(約 958 句/秒),見 build_stats.json(2026-07-11 單次量測) |
| FAISS IndexFlatIP 檔案大小 | ~205 MB(50,000 × 1024 × fp32) |
| Space 查詢延遲(ZeroGPU host CPU) | 編碼 ~1–3s + 檢索 <1ms |
| Space 冷啟動 | ~5 分鐘(裝套件 + 下載模型/索引 + 建 BM25) |
授權
- 程式碼:MIT
- 維基百科段落文字:CC BY-SA 4.0 © Wikipedia 貢獻者(每筆結果都附原始條目連結;索引資料的完整署名見 dataset repo)
Source repository
程式碼、訓練與評估流程、測試與完整證據都在 GitHub:https://github.com/kuotunyu/zhtw-wiki-semantic-search。GitHub kuotunyu 與 Hugging Face steven0226 為同一人。
Source code, the training/evaluation pipeline, tests and the full evidence trail live at https://github.com/kuotunyu/zhtw-wiki-semantic-search. GitHub kuotunyu and Hugging Face steven0226 are the same author.