CoolFace
Apppublic

steven0226/zhtw-wiki-semantic-search

sourceHugging Facemitupdated 23d agoView on Hugging Face
0likes
App README

🔍 繁中維基語意搜尋引擎(bge-m3 × FAISS × Gradio)

對繁體中文維基百科隨機抽樣的 50,000 個段落建立語意索引,並排比較 語意搜尋(bge-m3 + FAISS) 與 關鍵字搜尋(BM25 + jieba), 讓「同義改寫也搜得到」的差異一眼可見。

🚀 線上 Demo:[Hugging Face Space](https://huggingface.co/spaces/steven0226/zhtw-wiki-semantic-search) (免費 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 裡自己試)

重現步驟

powershell
# 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.