steven0226's picture
Link the encoding throughput claim to the committed build_stats.json
9a9282d verified
|
Raw
History Blame Contribute Delete
6.4 kB

A newer version of the Gradio SDK is available: 6.26.0

Upgrade
metadata
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.