OptETF Deploy
Deploy OptETF web app (cache-only on server, cross-platform paths, tz+dedup fixes) for HF Spaces
e8ccbfa
Raw
History Blame Contribute Delete
2.2 kB
"""BGE-M3 編碼器(離線版):從 app 內附的 encoder_model/bge-m3 載入,快取避免重載。
對齊 active_preference_v2/text_encoding.safe_encode 的關鍵設定:
BAAI/bge-m3(max_seq_length 8192, dim 1024)、normalize_embeddings=True、不加 prefix、
on_overflow='raise'(絕不靜默截斷)、use_safetensors=True(torch<2.6 必須)。
若 encoder_model/ 不存在,會 fallback 嘗試以 'BAAI/bge-m3' 線上下載(README 註明)。
"""
from __future__ import annotations
from pathlib import Path
import numpy as np
_CACHE: dict = {}
def get_encoder(model_path: str | Path, device: str | None = None):
import torch
from sentence_transformers import SentenceTransformer
if device is None:
device = "cuda" if torch.cuda.is_available() else "cpu"
model_path = str(model_path)
local = Path(model_path).exists()
key = (model_path, device)
if key not in _CACHE:
_CACHE[key] = SentenceTransformer(
model_path if local else "BAAI/bge-m3", device=device,
local_files_only=local, model_kwargs={"use_safetensors": True},
)
return _CACHE[key]
def safe_encode(texts, model_path, *, normalize: bool = True, on_overflow: str = "raise",
device: str | None = None):
"""回傳 (embeddings[N,1024], info)。超長 raise(防呆,與訓練一致)。"""
model = get_encoder(model_path, device=device)
max_len = int(model.max_seq_length)
texts = list(texts)
tk = model.tokenizer
tok_len = [len(tk.encode(t, add_special_tokens=True)) for t in texts]
n_over = sum(1 for L in tok_len if L > max_len)
info = {"max_seq_length": max_len, "max_token_len": max(tok_len) if tok_len else 0,
"n_over_limit": n_over, "truncated_ratio": float(n_over / max(1, len(texts))), "n": len(texts)}
if n_over > 0 and on_overflow == "raise":
raise ValueError(f"[safe_encode] {n_over}/{len(texts)} 筆超過 max_seq_length={max_len},已中止以免靜默截斷。")
emb = model.encode(texts, normalize_embeddings=normalize, convert_to_numpy=True,
show_progress_bar=False).astype(np.float32)
return emb, info